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.
Related
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.
How to display only some columns of Django model in a HTML template?
And also: how do I perform a function on one of the records? (amount)?
Right now I'm displaying a whole table of model like that:
my models.py
class Tabela(models.Model):
block_id = models.CharField(max_length=64)
timestamp = models.DateTimeField()
type = models.CharField(max_length=32)
link = models.CharField(max_length=64)
link_as_account = models.CharField(max_length=100)
account = models.CharField(max_length=100)
amount = models.CharField(max_length=64)
def __str__(self):
return self.block_id
My views.py
def search_results(request):
model = Tabela
query_addresse = request.GET.get('addressee', None)
query_hash = request.GET.get('hash', None)
if not query_hash and not query_addresse and request.method == 'GET':
return render(request, 'nanosite/index.html', {})
if query_hash and request.method == 'GET':
if query_addresse:
result = Tabela.objects.filter(account=query_addresse, block_id=query_hash)
else:
result = Tabela.objects.filter(block_id=query_hash)
field_names = [f.name for f in model._meta.get_fields()]
data = [[getattr(ins, name) for name in field_names]
for ins in result]
elif query_addresse and request.method == 'GET':
result = Tabela.objects.filter(account=query_addresse)
field_names = [f.name for f in model._meta.get_fields()]
data = [[getattr(ins, name) for name in field_names]
for ins in result]
return render(request, 'nanosite/index.html', {'field_names': field_names, 'data': data})
My index.html
<div id="bottomhalf" class="table-responsive">
<table class="table table-sm table-dark table-hover">
<thead class="thead-light">
{% for head in field_names %}
<th scope="col">{{ head }}</th>
{% endfor %}
</thead>
<tbody>
{% for row in data %}
<tr scope="row">
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
What I'd like to do is display only block_id, timestamp, account and amount in html. I've tried different approaches like using only the result part of views without field_names and data, but of course it didn't work.
My other question is, how can I modify the field amount and perform an operation on it to be displayed in template like amound divided by certain digit with a $ sign before it (for example if amount=1488 to be divided by 124 and displayed as '$12')?
Pass the queryset qs selecting the objects to display to the template and iterate over it to generate your table:
{% for obj in qs %}
<tr>
<td> {{obj.block_id}} </td>
<!-- etc ... -->
</tr>
{% endfor %}
Now, if you also want to pass a variable specifying the names of the fields of the object to tabulate, and in what order, you find out that the Django template engine is by design (!) incapable of doing that. You can either do what you are doing, and generate a list-of-rows in Python which you pass to the Template, or you need a Django custom template tag such as
#register.filter
def attr( obj, name):
return getattr( obj, name, '')
and then you can run an inner loop in your template
<tr>
{% for name in selected_field_names %}
<td> {{obj|attr:name}} </td>
{% endfor %}
</tr>
The answer to the second question, is to define a property on your model to return the field suitably transmogrified:
class Tabela(models.Model):
...
#property
def funny_amount(self):
val = self.amount/12.0
return f'$ {val:.2f}'
and refer to {{obj.funny_amount}} in your template
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 have two forms in my views, when I hit on save it's not working properly, when I want to display on my templates what I saved not showing as expected.
Here's what I have:
views.py
def index(request):
queryset = Personinfo.objects.all()
queryset2 = Person.objects.all()
qs = chain(queryset,queryset2)
form = personform(request.POST or None)
form2 = personinfoform(request.POST or None)
context = {
"queryset": queryset,
"queryset2": queryset2,
"qs": qs,
"form2":form2,
"form":form,
}
form2_valid = form2.is_valid()
form_valid = form.is_valid()
if form2_valid and form_valid:
a = form2.save()
b= form.save(commit=False)
b.ForeignkeytoA = a
b.save()
return render(request, "index.html", context)
index.html
<form method="POST" action="">{% csrf_token %}
{{form2.as_p}}
{{form.as_p}}
<input type="submit" value="Save!" />
</form>
<table >
<tr>
<th>Name</th>
<th>Address</th>
<th>Number</th>
<th>Hobbies</th>
</tr>
{% for item in qs %}
<tr>
<td>{{ item.name }}</td> #form2
<td>{{ item.address }}</td> #form1
<td>{{ item.phone_number }}</td> #form1
<td>{{ item.address }}</td> #form1
</tr>
{% endfor %}
</table>
models.py
class Personinfo(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Person(models.Model):
person = models.ForeignKey(Personinfo)
address = models.TextField()
phone_number = models.CharField(max_length=128)
hobbies =models.CharField(max_length=128)
def __str__(self):
return self.person
my output:
As you can see my table isn't showing my items as expected.
Is there a possible way to show every item in the same row?
Two errors are present. If I understand right, you're expecting the data from a Person instance and the data from its accompanying PersonInfo instance to print on the same line. However, you're trying to achieve this by using chain, which is not joining the querysets based on their relationship, but rather concatenating them blindly.
So if Person.objects.all() returns a queryset which contains the following data
id person address phone_number hobbies
1 1 a a a
2 2 5 5 5
and PersonInfo.objects.all() returns a queryset which contains
id Name
1 aaa
2 aa
chain combines them as
id person name address phone_number hobbies
1 aaa
2 aa
1 1 a a a
2 2 5 5 5
Instead, you should utilize the relationship between the models. If you pass only the Person queryset as context to your template, you could write
{% for p in persons %}
<tr>
<td>{{ p.person.name }}</td>
<td>{{ p.address }}</td>
<td>{{ p.phone_number }}</td>
<td>{{ p.hobbies }}</td>
</tr>
{% endfor %}
--
Additionally you are setting the Personinfo related instance incorrectly when you save your forms. By using b.ForeignkeytoA you are creating a new variable as a member of the object b called ForeignkeytoA, which has nothing to do with the Personinfo relationship. To set the related Personinfo, you should reference the name of the foreign key field, person. To correct this, that segment should be
# ...
b = form.save(commit = False)
b.person = a
b.save()
# ...