Django : Access model data in template using Class-based generic views - django

I'm trying to display the model data in the template.
When I don't have any filtering, it works fine!
class UserLogList(ListView):
context_object_name = 'data'
queryset = UserLog.objects.all().values('user_id__username','event_id','time')
But if I want to have some filtering, example if I want to get the details based on the user_id where user_id = 1. I can get the user id from the request object or self.request object.
So to achieve this, I did the following in my views.py
class DisplayUserActivity(ListView):
template_name = 'user_activity.html'
uid = self.request.user.id
def get_object(self):
uid = self.request.user.id
object = super(DisplayUserActivity,self).get_object()
object.data = UserLog.objects.filter(user_id = uid).values('user_id__username','event_id','time')
return object
and my template file:
{% block content %}
<h2> Logs </h2>
<table border="1" style="width:50%">
<tr>
<th> User Name </th>
<th> Description </th>
<th> Time </th>
</tr>
{% for user in data %}
<tr>
<td> {{user.user_id__username}}</td>
<td> {{user.event_id}}</td>
<td> {{user.time}} </td>
{% endfor %}
</tr>
</table>
{% endblock %}
What am I missing?

I think you need a method called get_queryset instead of get_object when extending the generic ListView.
EDIT:
Regarding your edit, theres an issue with your template
{% for user in data %}
should be
{% for user in object_list %}
But I suspect there are deeper problems here which are hard to decipher. My advice is to look through your code and the Django documentation more carefully, and possibly switch to a custom written view until you are more comfortable. :)

Related

Adding rows and columns to HTML table with Django

I have a problem. Consider an HTML table and it has rows and columns. I want to add a new row or column when I want it and I want it to be a record in the database. And I want to do this with django. What should I do?
I think I need to use django_table2 but I don't know how. I would be glad if you write a code sample. Thank you)
Say you have a model, you could get a list of objects like so;
def my_view(request):
context = {'object_list':MyModel.objects.all()}
return render(request, 'mypage.html', context)
Then in the template, you could do a few things to create tables:
Either, fully generate the table with Django, like so:
{% for object in object_list %}
<tr>
<td>{{object.data}}</td>
<td>{{object.data}}</td>
<td>{{object.data}}</td>
</tr>
{% endfor %}
This would create a new row for every object.
Another solution is:
{% for object in object_list %}
//Create row for every object
<tr>
{% for data in object.get_field_data %}
// Create column for every field in object
<td>{{data}}</td>
{% endfor %}
</tr>
{% endfor %}
Where get_field_data would be defined as a method on the model like so:
def get_field_data(self):
datalist = []
for field in self._meta.get_fields():
datalist.append(getattr(self, field.name))
return datalist
You could then even implement some checks on the get_field_data, for example, you could exclude fields.
def get_field_data(self):
datalist = []
for field in self._meta.get_fields():
if field.name != 'id':
datalist.append(getattr(self, field.name))
return datalist
You don't need to use any external package, you can do this easily by using the Django Template Language
Here is a code example using a ListView
# views.py
from django.views.generic import ListView
class ItemListView(ListView):
template_name = 'mytable.html'
model = MyModel
context_object_name = 'item_list'
<!-- mytable.html -->
...
<table>
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
{% for item in item_list %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.amount }}</td>
</tr>
{% endfor %}
</tbody>
</table>
...
In this way your template will have access to a list of objects called item_list which corresponds exactly to the records in the database table. You can cycle that list/queryset using a for loop that will automatically wrap that content in the html needed to make it part of the table.

How can I dynamically reference the fields in a Django query set object in a template?

I have a model that is referenced by a generic ListView, and feeds into a template. Attempts to create a table in the template give me a TypeError: not iterable - what am I doing wrong?
Sample code
Class bookmodel(models.Model):
Book = models.CharField(max_length=255)
Author = models.CharField(max_length=255)
Views
Class bookview(generic.ListView):
model = bookmodel
template = “books.html”
Which generates an object_list something like:
<Queryset [<bookmodel: Grapes of Wrath >, <bookmodel: I, Robot>]>
The template is laid out as follows:
{% extends ‘base.html’ %}
{% block content %}
<table>
<thead>
<tr>
<th> book </th>
<th> author </th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr>
{% for field in object %}
<td> {{ field }} </td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
But this fails with the aforementioned error.
The linked question kindly suggested by #Karioki in his comment proposed the following code:
def attrs(self):
for field in self._meta.fields:
yield field.name, getattr(self, field.name)
The yield function inexplicably didn't work for me in my template either from a view or a template tag:
The following derivative code did:
def get_values(self):
value_set = [getattr(self, field.name) for field in self._meta.fields]
return value_set
While this still feels clunky to me, I'm marking it as a straw man answer but am hopeful that there will be another answer that provides a more efficient way to investigate a database record in the template in Django.

Display Django model data to table in HTML

I have two Django models that record time. Model one records time during the morning and Model two records time during the evening. I want to present both of these times along with the difference between the times within an HTML table but am confused about how to do it. I am new to Django and would really appreciate some advice.
This is what I have so far:
models.py:
class Alltime(models.Model):
id= models.ForeignKey(User, on_delete = models.CASCADE)
mtime = models.DateTimeField()
etime = models.DateTimeField()
views.py:
def panel(request):
time_data = User.objects.filter(pk__gt=1) #I need all data except for the default Super User account
get_time = Alltime.objects.all()
return render(request, 'users/interface.html', {'data': time_data, "get_time": get_time})
panel.html:
<form>
{% csrf_token %}
<table>
<tr>
<th>Name</th>
<th>Morning timeE</th>
<th>Evening time</th>
<th>Difference in hours</th>
</tr>
{% for data in data %}
<tr>
<td>{{data.username}}</td>
{% endfor %}
{% if get_time %}
{% for m in get_time %}
<td>{{m.mtime}}</td>
<td>{{m.etime}}</td>
{% endfor %}
{% else %}
<td> Not available </td>
{% endif %}
</tr>
</table>
</form>
How can I get the difference between the times and place them within the HTML table?
If I understand correctly what you want to do, then you can/need to structure your data differently. An easy way is to prepare the data in your view:
def panel(request):
time_data = User.objects.filter(pk__gt=1)
time_table=[]
for user in time_data:
morning_time = Morning.objects.filter(user=user)
evening_time = Evening.objects.filter(user=user)
diff = morning_time - evening_time
time_table.append((user.name, morning_time, evening_time, diff))
return render(request, 'users/interface.html', {'data': time_table})
And in the template:
<table>
<tr>
<th>Name</th>
<th>Morning timeE</th>
<th>Evening time</th>
<th>Difference in hours</th>
</tr>
{% for line in data %}
<tr>
<td>{{line.0}}</td>
<td>{{line.1}}</td>
<td>{{line.2}}</td>
<td>{{line.3}}</td>
</tr>
{% endfor %}
</table>
You need to add the handling of not existing data in the view code.
Some remarks:
The whole thing does not really make sense to me. I guess you will need to filter for dates too. But you should get the idea from this. And why is it in a form?
You can add a property to the Alltime model that returns the difference between the morning and evening time
#property
def diff(self):
return self.etime - self.mtime
Then in your template you can use this property
{% for m in get_time %}
<td>{{m.mtime}}</td>
<td>{{m.etime}}</td>
<td>{{m.diff}}</td>
{% endfor %}

Django:The QuerySet value for an exact lookup must be limited to one result using slicing

I am working on a project where admin can assign team to manager. But it is not working and i have no idea how it will work. Because it is raising an error saying "The QuerySet value for an exact lookup must be limited to one result using slicing."
Here is my model.py
class manager(models.Model):
name = models.CharField(max_length= 500)
designation = models.CharField(max_length= 500)
user = models.ForeignKey(User,on_delete=models.CASCADE)
class Meta:
permissions = [
("edit_task", "can edit the task"),
]
here is my views.py file for the teams of manager
#login_required (login_url= 'have the url where it will go')
#permission_required('have the permission that is assigned by me')
def supervisor(request):
return render(request, 'manager/index-3.html')
def supervisor_team(request):
print(request.user.email)
email=request.user.email
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.filter(user__email=email))
return render(request, "manager/accept_team.html", {"object": obj})
here is my template
<div class="body table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>S No.</th>
<th>COMPANY NAME</th>
<th>TEAM MEMBER</th>
<th>EMAIL</th>
</tr>
</thead>
<tbody>
{%for object in team%}
<tr>
<form id="form_id" method="POST" action = "#">
{% csrf_token %}
<th scope="row"> {{ forloop.counter }}</th>
<td>{{object.company_name}}</td>
<td>{{object.team_member}}</td>
<td>{{object.email}}</td>
<td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
I have no idea where i am wrong.
I think this is the problem:
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.filter(user__email=email))
if you want to filter by one manager you have to use get instead of filter:
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.get(user__email=email))
But if you want to filter by several managers you need to use __in:
obj= Create_Team.objects.filter(status='Accept',
managers__in=manager.objects.filter(user__email=email))
Also you are passing {"object": obj}) to template but in templare you are trying to iterate over team. So change it to pass team variable:
return render(request, "manager/accept_team.html", {"team": obj})

Django - ModelForm - UpdateView -pk

I would like to implement UpdateView in order to update selected objects of my Model.
Currently I have:
views.py
def fd_detail(request, fdslug):
fddetail = Fd.objects.get(slug=fdslug)
cffilter = CashFlowFilter(request.GET, queryset=CashFlow.objects.filter(feeder__slug=fdslug))
return render(request, 'fd/fd_detail.html',
{'fddetail': fddetail,
'cffilter': cffilter,
})
class CashFlowUpdate(UpdateView):
model = CashFlow
fields = ['amount', 'description']
url.py
path('<slug:fdslug>/updatecashflow/', views.CashFlowUpdate.as_view(),name = "update_cashflow"),
path('<slug:fdslug>/', views.fd_detail, name="fd_detail")
update_cashflow.html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update">
</form>
I get the following error : " Generic detail view CashFlowUpdate must be called with either an object pk or a slug in the URLconf." , which means i need to pass-through the primary key of the object I want to Update... What is the best way to do it? Currently all my objects are displayed via a table (see below)? i could add a column with a url link "Edit", but how to put the pk in it?
<table class="table table-bordered">
<thead>
<tr>
<th>Amount</th>
<th>Description</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
{% for cashflow in cffilter.qs %}
<tr>
<td> {{cashflow.amount}} </td>
<td> {{cashflow.description}} </td>
<td> ?????? </td>
</tr>
{% empty %}
<tr>
<td colspan="5"> No cashflow matches your search criteria</td>
</tr>
{% endfor %}
</tbody>
</table>
many thanks
Like the error says:
Generic detail view CashFlowUpdate must be called with either an object pk or a slug in the URLconf.
An update view usually needs some kind of URL parameter to determine which object to update. It implicitly aims to check for two fields: the slug and the primary key. But here in your urls.py, you used fdslug as a URL parameter.
This can easily be resolved by specifying a slug_url_kwarg [Django-doc] yourself, like:
class CashFlowUpdate(UpdateView):
model = CashFlow
fields = ['amount', 'description']
slug_url_kwarg = 'fdslug'
As for an edit link, you can add a {% url ... %} template tag, like:
<td>edit</td>