I am new to Django and Python,
I have a table for clients, and one for trips. I can display the clients on the clients page, and the same for the trips.
But on the clients page, I want to show all the trips that are linked to each client. And this is where I hit a wall.
This is my models.py
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Clientes(models.Model):
nome = models.CharField(max_length=30)
apelido = models.CharField(max_length=30)
morada = models.CharField(max_length=200)
tel = models.CharField(max_length=9, validators=[RegexValidator(r'^\d{1,10}$')])
nif = models.CharField(max_length=9, validators=[RegexValidator(r'^\d{1,10}$')])
def __str__(self):
return "%s %s" % (self.nome, self.apelido)
class Meta:
verbose_name_plural = "Clientes"
class Viagem(models.Model):
trip_id = models.CharField(max_length=30)
cliente = models.ForeignKey(Clientes, on_delete=models.CASCADE)
comp = models.CharField(max_length=30)
data = models.DateField()
destino = models.CharField(max_length=30)
def __str__(self):
return self.trip_id
class Meta:
verbose_name_plural = "Viagens"
This is my views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Clientes, Viagem
# Create your views here.
def index(request):
ls= Clientes.objects.all()
context = {'ls': ls}
return render(request, "booking/home.html", context)
def cliente(request, id):
ls= Clientes.objects.filter(id=id)
context = {'ls': ls}
return render(request, "booking/cliente.html", context)
def trip(request):
ls= Viagem.objects.all()
context = {'ls': ls}
return render(request, "booking/trip.html", context)
and this is the table on the home.html
<table id="selector" class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Nome</th>
<th>Apelido</th>
<th>Morada</th>
<th>Telemóvel</th>
<th>NIF</th>
<th>Viagens</th>
</tr>
</thead>
<tbody>
{% for ls in ls %}
<tr>
<td>{{ls.nome}}</td>
<td>{{ls.apelido}}</td>
<td>{{ls.morada}}</td>
<td>{{ls.tel}}</td>
<td>{{ls.nif}}</td>
<td>{{ls.trip_id}}</td>
</tr>
{% endfor %}
</tbody>
</table>
I assume it has something to do with ForeignKey, but a ForeignKey on the first class won't work.
I thought about creating a new def on the views.py using the Viagem table and a diferent context, but that also doesn't seem to be the solution.
So, anyone can point me in the right direction?
In your views.py, specifically the cliente function, you want to add the following line:
viagems=Viabem.objects.filter(cliente=ls)
modify the following line:
ls= Clientes.objects.filter(id=id)
so that it now shows:
ls=Clientes.objects.get(id=id)
and then change your context to equal:
context = {'ls': ls, 'viagems': viagems}
then you will be able to iterate over the different viagems in your html template file with the following kind of structure:
{% for viagem in viagems %}
{{ viagem.whatever_field }}
{% endfor %}
and that should be it, I believe...
Since you are filtering by ID (fetching single object), this is one of the options to go:
views.py
from django.shortcuts import get_object_or_404
def cliente(request, id):
ls = get_object_or_404(Clientes, id=id)
trips = obj.viagem_set.all()
context = {'ls': ls, 'trips': trips}
return render(request, "booking/cliente.html", context)
template.html
<table id="selector" class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Nome</th>
<th>Apelido</th>
<th>Morada</th>
<th>Telemóvel</th>
<th>NIF</th>
<th>Viagens</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ls.nome}}</td>
<td>{{ls.apelido}}</td>
<td>{{ls.morada}}</td>
<td>{{ls.tel}}</td>
<td>{{ls.nif}}</td>
<td>{% for trip in trips %}{{ trip.id }}{% endfor %}</td>
</tr>
{% endfor %}
</tbody>
While I'm not sure why you need to show record ID's in the table.
#João de Sousa, I found you want to show all trips that are associated with each client.
First of all you have to get a single client by his id obj = clients.objects.get(id = id) then this id pass to referred model like this obj1 = Viagem.objects.filter( id = obj.id)
According to my understanding from your above posted question is that you want to get help about how you get data from reference model/table. Django provides very easy and flexible way to get data from tables.
Feel free to ask any question if you need more clarification.
Related
I tried getting the list of objects from the current logged users. There something missing in the codes.
I wrote a class-based view as well as function-based views.
Class-based views give an error like 1 positional argument but two were given.
And in function-based view it giving only first item instead looping through it.
I want to show the pass investments inventory record of each investor.
Thank you!
views.py (Function-Based Views)
def InvestmentListView(request):
investors = Investment.objects.all(id=request.user.id)
args = {'investors':investors}
return render(request, 'investors/myinvest.html', args)
This only retrieving an only first item.
views.py (class-Based viewa)
class InvestmentListView(ListView):
model = Investment
template_name = 'investors/myinvest.html'
context_object_name = 'total_invested_by_user'
def get_queryset(self):
return Investment.objects.filter(investor=self.request.user.id)
This CBV gives an error like 1 positional argument, but 2 were given.
myinvest.html
<div class="container">
{% if user.is_authenticated %}
<h2>Investor Name: {{ request.user }}</h2>
<table>
<tr>
<th>Amount Invested</th>
<th>Date</th>
<th>Rate Of Interest</th>
<th>Return</th>
<th>Profit</th>
</tr>
<tr>
{% for invest in investors %}
<th>{{ invest.amount }}</th>
<th>{{ invest.timestamp }}</th>
<th>{{ invest.rate }}</th>
<th>None</th>
<th>None</th>
{% endfor %}
</tr>
</table>
{% endif %}
Here, models.py
class Investor(models.Model):
name = models.CharField(max_length=99)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Investment(models.Model):
amount = models.FloatField(blank=False)
rate = models.FloatField(blank=False)
timestamp = models.DateField(default=datetime.now)
investor = models.ForeignKey(Investor, on_delete=models.CASCADE)
def __str__(self):
return str(self.investor)
You are filtering the Investment id with your user id which is not correct. This should work:
investors = Investment.objects.filter(investor__user=request.user)
While rendering a table of items, if the value for field X is not defined, it is rendered as a select element.
Django makes a query for each select element and these can add up and cause delays in large tables.
What is the best way to reduce the number of queries?
views.py
from rest_framework import renderers
from rest_framework.response import Response
class ItemViewSet(viewsets.ModelViewSet):
queryset = models.Item.objects.select_related("bought_by")
serializer_class= serializers.ItemSerializer
filterset_fields = ("bought_by")
renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.TemplateHTMLRenderer]
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
if request.accepted_renderer.format == "html":
items = list()
for item in queryset:
items.append({"serializer": self.get_serializer(item), "item": item})
return Response(
{
"items_info": items,
"style": {"template_pack": "rest_framework/inline/"},
},
template_name="myapp/items_list.html",
)
else:
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
items_list.html
{% load static %}
{% load rest_framework %}
{% if items_info %}
{% csrf_token %}
<table id="Items_Table" class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Active</th>
<th scope="col">Bought By</th>
</tr>
</thead>
<tbody>
{% for pair in items_info %}
<tr scope="row">
<td>{{ pair.item.name }}</td>
<td>{{ pair.item.active }}</td>
<td>
<form action="{{ item.url }}" method="PATCH">
{% render_field pair.serializer.bought_by style=style %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-center">No items to show.</p>
{% endif %}
If there are three items, one query each will be made to get serializer.bought_by. I thought Django/DRF would just reuse the value, but it's querying for each loop.
Trying to pass "serializer-bought-by": self.get_serializer().bought_by in the response, I get an AttributeError: 'ItemSerializer' object has no attribute'bought_by'
Printing the serializer I can see:
>>>print(self.get_serializer())
ItemSerializer(context={'request': <rest_framework.request.Request object>, 'format': None, 'view': <myapp.views.ItemViewSet object>}):
url = HyperlinkedIdentityField(view_name='myapp:item-detail')
name= CharField(unique=True, max_length=50)
active = BooleanField(required=False)
bought_by = SlugRelatedField(allow_null=True, queryset=<QuerySet [<Buyer: James>, <Buyer: John>, ...]>, required=False, slug_field='name')
Is there a way to pass the bought_by to the template?
Or do I have to use JS; render the form field outside the loop and clone/duplicate somehow?
---Edits---
As per Endre's request: models.py
class Buyer(models.Model):
name = models.CharField(unique=True, max_length = 20)
class Item(models.Model):
name = models.CharField(unique=True, max_length = 50)
active = models.BooleanField(default=True)
bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name",)
You cannot access the value of fields directly in the serializer. You should use serializer.data['bought_by'] instead. Better of, don't pass the serializer to the template, instead pass the serializer.data.
As for the queries in the template, no query is made to the DB to get the bought_by since it's already included in the serializer data. You're just showing one field name from bought_by, which is already preloaded in the serializer data. The select_related you applied also ensures that multiple queries are not made to fetch bought_by during serialization.
I need help with solving an issue regarding duplicate database query's for each form in an inlineformset. I have a page where users can add and edit books belonging to an author.
models.py
from django.db import models
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Book(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
category_idcategory = models.ForeignKey(Category, models.DO_NOTHING)
class Category(models.Model):
name = models.CharField(max_length=100)
forms.py
from django import forms
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = '__all__'
views.py
instance = get_object_or_404(Author, id=id)
form = inlineformset_factory(Author, Book, form=BookForm, can_delete=True, extra=5)
formset = form(request.POST or None, instance=instance)
if request.method == "POST":
if formset.is_valid():
instanceForm = formset.save(commit=False)
for obj in instanceForm:
obj.save()
for obj in formset.deleted_objects:
obj.delete()
return HttpResponseRedirect(URL)
When I run the template, it performs a database query to the Category model for each form in formset. How do I prevent those duplicates? I dont know where to put select_related or prefetch_related. If instances of Book model grows to a large number, the page load times are getting very slow.
template.html
<table class="table table-sm">
{{ formset.management_form }}
<thead>
<td>Title</td>
<td>Category</td>
<td>delete</td>
</thead>
<tbody>
{% for obj in formset %}
{{ obj.id }}
<tr>
<td>{{ obj.title }}</td>
<td>{{ obj.category_idcategory }}</td>
<td>{{ obj.DELETE }}</td>
</tr>
{% endfor %}
</tbody>
</table>
You can change the queryset of a formset like this:
class InlineBookFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Here is where to put the select_related.
self.queryset = Book.objects.select_related('category_idcategory').all()
then in your factory method call:
# Use your custom InlineBookFormSet
form = inlineformset_factory(Author, Book, form=BookForm, can_delete=True, extra=5, formset=InlineBookFormSet)
I am trying to post multiple values from different models in a for loop in a template. It is not doing what I am planning. I want to display a count of LeadActions that belong to Leads in a table. I did comment the part out that is not working as well. The table should display the list of Leads and the the count of how many overdue actions(LeadActions) there are for that specific lead.
My View
class LeadListView(LoginRequiredMixin, generic.ListView):
login_url = '/scrty/login/'
template_name = "nodiso/leadslist.html"
model = models.Leads
def get_context_data(self, **kwargs):
ctx = super(LeadListView, self).get_context_data(**kwargs)
ctx['actions']= models.LeadActions.objects.all()
return ctx
def get_queryset(self):
return models.Leads.objects.filter(company=self.request.session['compid'],archive=False)
My template
<table class="table">
<thead>
<th>Name</th>
<th>Overdue Tasks</th>
<th>Total Tasks</th>
</thead>
{% for lead in leads_list %}
{# {% for action in action_list %}#}
<tr>
<td>{{lead.name}}</td>
<td><span class="badge">{{ actions.name|length }}</span></td>
<td><span class="badge">42</span></td>
</tr>
{# {% endfor %}#}
{% endfor %}
</table>
The Models
class LeadActions(models.Model):
lead = models.ForeignKey(Leads)
name = models.CharField(max_length=265)
crdate = models.DateField(auto_now_add=True)
Duedate = models.DateField()
creator = models.CharField(max_length=265)
overdue = models.IntegerField(null=True,blank=True)
def __str__(self):
return self.name
class Leads(models.Model):
company = models.ManyToManyField(Company)
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
name = models.CharField(max_length=265)
email = models.EmailField(max_length=265)
tel = models.IntegerField()
archive = models.BooleanField(default=False)
dateenq = models.DateField(auto_now_add=True,null=True)
def get_absolute_url(self):
return reverse('nodisoapp:leadlist')
def __str__(self):
return self.name
You shouldn't be sending the list of actions from the view. Instead, in the template, you can access {{ lead.leadactions_set.count }} to give the count of LeadActions related to each Lead in the loop.
I'm having a problem hope you could help me please.
I got these models:
MODELS = (
('A', 'A'),
('B', 'B'),
)
class person(models.Model):
nombre = models.CharField(max_length=128)
model = models.CharField(max_length=128,choices=MODELS, default=True)
def __str__(self):
return self.nombre
class person_A(models.Model):
person = models.ForeignKey(person, on_delete=models.PROTECT, null=True)
hobbies = models.CharField(max_length=40, default='')
def __str__(self):
return self.person.nombre
class person_B(models.Model):
person = models.ForeignKey(person, on_delete=models.PROTECT, null=True)
age = models.IntegerField(default=10)
def __str__(self):
return self.person.nombre
As you can see I have person_A, person_B which will be saved in my database.
Here's my view:
def get_person(request):
titulo = 'Person'
qs_a = person_A.objects.all()
qs_b = person_B.objects.all()
qs = chain(qs_b,qs_a)
form = person_aForm(request.POST or None)
form2 = personForm(request.POST or None)
form4 = person_bForm(request.POST or None)
context = {
"qs_a": qs_a,
"qs_b": qs_b,
"qs": qs,
"form2": form2,
"form": form,
"form4": form4,
"titulo": titulo,
}
form2_valid = form2.is_valid()
form_valid = form.is_valid()
form4_valid = form4.is_valid()
if form2_valid:
person = form2.save()
if form_valid:
person_a = form.save(commit=False)
person_a.person = person
person_a.save()
messages.success(request, 'Se ha guardado satisfactoriamente')
return redirect("get_person")
if form4_valid:
person_b = form4.save(commit=False)
person_b.person = person
person_b.save()
messages.success(request, 'Se ha guardado satisfactoriamente')
return redirect("get_person")
return render(request, "person.html", context)
Here as you can see is being saved my form for person_A or person_B.
Here's my delete function:
def delete_person(request,id):
try:
qs = person_B.objects.all()
instance = get_object_or_404(qs, id=id)
instance.delete()
messages.success(request, 'B')
except:
try:
qs = person_A.objects.all()
instance = get_object_or_404(qs, id=id)
instance.delete()
messages.success(request, 'A')
except:
pass
return redirect('get_person')
My url:
url(r'^delete_person/(?P<id>\d+)/$',views.delete_person, name='delete_person'),
Template:
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Last Name</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for item in qs %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.person.model }}</td>
<td>{{ item.person.nombre }}</td>
<td>Delete</td>
</tr>
{% endfor %}
</tbody>
</table>
Basically, I'm having two different models which everyone is being saved in the database and being shown in a table. To do that I used chain() from itertools. I joined Person A and Person B to show them both in one table. This is working fine, now here's my problem:
If person_A has the same id as person_B and I want to delete person_A, it will delete person_B instead. I know this is because of the chain() but i'd like to know if there's a possible way to solve this?
Thank you!
Your problem lies in the fact that delete_function() always tries to delete person_B first. Unless an exception is thrown, person_A will never be deleted. If an exception is thrown, you will still delete person_B first, since it is inside try: block, hence an attempt to delete it always precedes an attempt to delete person_A. By the way, the way your code is written now, any exception will result in an attempt to delete person_A. I would suggest using a more specific exception, or otherwise you might run into really confusing bugs later on.
As for the solution, I'm sure there are more clever ways out, but couple of quick-and-dirty solutions I can think of right now:
Defining a custom filter, which would check what type of class the person is and act accordingly (e.g., call different delete methods for different classes; pass a flag as query parameter and use it in the delete_person() method to determine which person_ to delete).
What I would probably do: instead of chaining instances of models, iterate over them separately and call delete methods suited for a specific model:
<table>
<thead>...</thead>
<tbody>
...
{% for item in qs_a %}
<tr>
<td>... delete_person_A() ...</td>
</tr>
{% endfor %}
{% for item in qs_b %}
<tr>
<td>... delete_person_B() ...</td>
</tr>
{% endfor %}
</tbody>
</table>
If I recall correctly, chain() iterates over first iterable until it is exhausted, and only then does it iterate over the second one. Hence, iterating over the qs_a and qs_b separately should not make a huge difference in the output. While this way you will have to write slightly more code, it will also be a bit more simple and explicit - hence, easier to understand and refactor later, if there is a need.