I'm interested in learning how to display all attributes of a list of querysets that come from different models.
Here's an example:
models.py
class MyModelA(models.Model):
attr1 = something
attr2 = something
class MyModelB(models.Model):
attr3 = something
attr4 = something
class MyModelC(models.Model):
attr5 = something
attr6 = something
views.py
Let's say we have three model instances that are stored in a list:
all_selected_queries = [mymodela, mymodelb, mymodelc]
For each queryset in the list, I want to display all model field titles and data in a template.
My approach:
# Loop through the list and get the verbose name title of each field ("titel")
for z in all_selected_queries:
queryset_fields = z._meta.get_fields()
for f in queryset_fields:
titel = f.verbose_name.title()
return titel
What challenges me is how to get the fields' values without having to include the actual attribute name (because they are different for each queryset).
So instead of explictly calling
f.attr1, f.attr2, f.attr3, f.attr4, f.attr5
for each field, I'd like to encounter a solution that works across model boundaries.
Thank you very much for your help!
You can try like this:
v_list = list()
for z in all_selected_queries:
queryset_fields = z._meta.get_fields()
values = dict()
for f in queryset_fields:
values[f.verbose_name.title()] = getattr(z, f.attname)
v_list.append(values)
return render(request,'some_template.html',{'values':v_list})
And show them in template:
{% for value in values %}
{% for key, val in value.items %}
<b>{{ key }}: </b>{{ val }}
{% endfor %}
{% endfor %}
Related
models equipement :
class Equipement(models.Model):
nom_equipement=models.CharField(max_length=60)
qte_stock=models.IntegerField()
panne=models.ManyToManyField(Panne)
models intervention :
class Intervention(models.Model):
Titre_intervention = models.TextField(max_length=255)
date_intervention = models.DateField(auto_now_add=True)
type_panne = models.ForeignKey(Panne,on_delete=models.CASCADE)
etat = models.CharField(max_length=30)
description = models.TextField(max_length=255)
image = models.ImageField(blank=True,null=True,upload_to='medial/%Y/%m/%D')
equipements = models.ManyToManyField(Equipement)
clients = models.ForeignKey(Client,on_delete=models.CASCADE,default=True)
models intervention with relationship manytomany :
so when I add a new "intervention" it will add to table of association
I need to list all equipment of each intervention this is my view :
def mes_intervention(request):
if 'id_client' in request.session:
get_idClient=request.session['id_client']
Interv_client = Intervention.objects.all().filter(clients=get_idClient)
context = {
'intervention':Interv_client
}
return render(request, 'clients/mes-intervention.html',context)
and this is where I list all intervention into template html
As a side note, you have plurals and singulars a bit mixed up. A foreign key points to one model:
client = models.ForeignKey(Client,on_delete=models.CASCADE,default=True) # singular
A queryset returns multiple objects:
context = { 'interventions': Interv_client}
While you cannot call functions in templates with arguments, you can invoke object methods without arguments. So, you can in fact do this:
{% for item in interventions %}
...
{% for equipment in item.equipements.all %}
{{ equipment.qte_stock }}
{% endfor %}
{% endfor %}
I have two models as below:
class Loo(models.Model):
loo_type = models.CharField(max_length=3, default="LOO"...)
loo_from = models.ForeignKey(Harr, on_delete=models.CASCADE, ...)
loo_fac = models.DecimalField(max_digits=7, decimal_places=.....)
class Woo(models.Model):
woo_item = models.AutoField(primary_key=True, ...)
woo_loo = models.ForeignKey(Loo, on_delete=models.CASCADE, ...)
woo_dt = models.DateField(null=True, ...)
woo_rate = models.DecimalField(max_digits=7, decimal_places=.....)
I am trying to display data from the models using the following listview:
class WhoLooView(ListView):
template_name = "who_loo_list.html"
context_object_name = 'wholoos'
model = Loo
def get_context_data(self, **kwargs):
context = super(WhoLooView, self).get_context_data(**kwargs)
context.update({
'woo_item_list': Woo.objects.order_by('-woo_dt'),
})
return context
def get_queryset(self):
return Loo.objects.order_by('loo_from')
Note that there can be more than one "woo_item" per instance of Loo (id), so in the listview there will be occasions when for the same Loo id it will have two instances of Woo/s, and thus need to be displayed separately (preferably in two distinct rows).
What I have tried so far creates extra (adjacent) columns for each Loo id and whichever Loo/s have a single instance of Woo, are shown in the normal fashion as expected.
How does one take care of such a situation. Can we have a nested row for cases where there are more than one instance of Woo?
Edit
What I have tried (based on your code sample):
{% for obj in wholoos %} <!-- wholoos : context object name -->
{{ obj.loo_type }}
{% for item in obj.woo_set.all %}
{{ item.woo_dt }}
{% endfor %}
{% endfor %}
But now I am not getting anything from the second model Woo.
Edit 2
I am getting the same result as earlier with my original code. Check the image below:
If you notice (in the image above), objects # 31 and 34 have one each of child objects (sub-objects). # 32 and 33 have two each. I want them to be in separate rows and not columns. The reason is, in case there are a number of items for each parent item (which my DB design makes it imperative to be), I would end up with an enormous number of extra columns for the sub-objects (and that too without any column header).
you can loop the instances of Loo as shown, in your templates, don't have to override the get_context_data.
{% for obj in object_list %}
{{ obj.loo_type }}
{% for item in obj.woo_set.all %}
{{ item.woo_dt }}
{% endfor %}{% endfor %}
class Order(models.Model):
name = models.CharField(max_length=100)
# other fields..
user = models.ForeginKey(User)
old = models.BooleanField(default=False)
I want to display all the orders of a specific user, but I want to split them those which are "old" and the ones who are not.
So, currently I do in views.py:
orders = Order.objects.filter(user=user)
In template:
First table:
<table>
{% for order in orders %}
{% if not order.old %}
<tr>
<td>... </td>
</tr>
{% endif %}
{% endfor %}
</table>
And another table:
{% for order in orders %}
{% if order.old %}
<tr>
<td>...</td>
<tr>
{% endif %}
{% endfor %}
This way have some drawbacks, first, now I want to count how many of the orders are "old", to display this number in the template. I can't, unless I do another query.
Is it possible to annotate(number_of_old=Count('old'))? Or I have to do another query?
So what would be the best?
1. Do two queries, one with old=False, another with old=True, and pass two querysets to the template. And use |len filter on the querysets
2. Do one query like this and split them somehow in python? That will be less convenient as I have a similar structures which I want to split like that.
And should I call the DB .count() at all?
EDIT:
If I would write my model like this:
class Order(models.Model):
name = models.CharField(max_length=100)
# other fields..
user = models.ForeginKey(User)
old = models.BooleanField(default=False)
objects = CustomManager() # Custom manager
class CustomQueryset(models.QuerySet):
def no_old(self):
return self.filter(old=False)
class CustomManager(models.Manager):
def get_queryset(self):
return CustomQuerySet(model=self.model, using=self._db)
Is this template code produce one or two queries ?
{% if orders.no_old %}
{% for order orders.no_old %}
...
{% endfor %}
{% endif %}
You can't do any annotations, and there is no need to make .count() since you already have all the data in memory. So its really just between:
orders = Order.objects.filter(user=user)
old_orders = [o for o in orders if o.old]
new_orders = [o for o in orders if not o.old]
#or
old_orders = Order.objects.filter(user=user, old=True)
new_orders = Order.objects.filter(user=user, old=False)
In this specific scenario, I don't think there will be any performance difference. Personally I will choose the 2nd approach with the two queries.
A good read with tips about the problem: Django Database access optimization
Update
About the custom Manager which you introduce. I don't think you are doing it correctly I think what you want is this:
class CustomQueryset(models.QuerySet):
def no_old(self):
return self.filter(old=False)
class Order(models.Model):
name = models.CharField(max_length=100)
# other fields..
user = models.ForeginKey(User)
old = models.BooleanField(default=False)
#if you already have a manager
#objects = CustomManager.from_queryset(CustomQueryset)()
#if you dont:
objects = CustomQueryset.as_manager()
So having:
orders = Order.objects.filter(user=user)
If you do {% if orders.no_old %} will do another query, because this is new QuerySet instance which has no cache..
About the {% regroup %} tag
As you mention, in order to use it, you need to .order_by('old'), and if you have another order, you can still use it, just apply your order after the old, e.g. .order_by('old', 'another_field'). This way you will use only one Query and this will save you one iteration over the list (because Django will split the list iterating it only once), but you will get less readability in the template.
So Im trying to create a similar panel like django admin and while I have my tables set up with query information and filters, Im trying to copy the "editable" option by creating fields that can be edited in the table. My problem is lets say I have models:
class model1(model.Models):
name = CharField(max_length = 20)
phone = IntegerField(max_length = 20)
def __unicode__(self):
return (#I Just return the name and phone number as a string here)
class model2(model.Models):
name = ForeignKeyFeild(model1)
team = CharField(max_length = 20)
If I set up a modelformset factory like so in a view:
qset = model2.objects.all()
fset = modelformset_factory(model2)
form = fset(queryset = qset)
In templates how can I show the value of 'phone' from model1?
When I render the form in the template:
{% for f in form %}
{{f.team.value}} # this gives me the value without an input field
{{f.name.phone.value}} # this however renders nothing and that's what I'm trying to find out
{%endfor%}
Also How can I make it so that {{f.name.value}} shows me the unicode string and not the actual numerical value (ID)?
How can I make the {{f.name.phone}} which is information from the table where it holds a foreign_key show?
If not is there a better way of doing this?
The original model object is available as the instance attribute of the form:
{% for f in form %}
{{ f.instance.team }}
{{ f.instance.name.phone }}
{% endfor %}
My data model is simple:
class Neighborhood(models.Model):
name = models.CharField(max_length = 50)
slug = models.SlugField()
class Location(models.Model):
company = models.ForeignKey(Company)
alt_name = models.CharField()
neighborhoods = models.ManyToManyField(Neighborhood)
I would like to supply a page on my site that lists all locations by their neighborhood (s). If it were singular, I think {% regroup %} with a {% ifchanged %} applied to the neighborhood name would be all that I need, but in my case, having it be a m2m, I'm not sure how do this. A location may have multiple neighborhoods, and so I would like them to be redundantly displayed under each matching neighborhood.
I'm also aware of FOO_set but that's per Object; I want to load the entire data set.
The final result (in the template) should be something like:
Alameda
Crazy Thai
Castro
Kellys Burgers
Pizza Orgasmica
Filmore
Kellys Burgers
Some Jazz Bar
Mission
Crazy Thai
Elixir
...
The template syntax would (ideally?) look something like:
{% for neighborhood in neighborhood_list %}
{% ifchanged %}{{ neighborhood.name }}{% endifchanged %}
{% for location in neighborhood.restaurants.all %}
{{ location.name }}
{% endfor %}
{% endfor %}
I'd just do it the expensive way and cache the result over scratching my head. Your template example would work fine until performance becomes an issue in generating that one page per X cache timeout.
You could do it in python as well if the result set is small enough:
# untested code - don't have your models
from collections import defaultdict
results = defaultdict(list)
for location_m2m in Location.neighborhoods.through.objects.all() \ # line wrap
.select_related('neighborhood', 'location', 'location__company__name'):
if location_m2m.location not in results[location_m2m.neighborhood]:
results[location_m2m.neighborhood].append(location_m2m.location)
# sort now unique locations per neighborhood
map(lambda x: x.sort(key=lambda x: x.location.company.name), results.values())
# sort neighborhoods by name
sorted_results = sorted(results.items(), key=lambda x:x[0].name)
create another model that would define your m2m relationship:
class ThroughModel(models.Model):
location = models.ForeignKey('Location')
neighborhood = models.ForeignKey('Neighborhood')
and use it as the through model for your m2m field:
class Location(models.Model):
...
neighborhoods = models.ManyToManyField(Neighborhood, through='ThroughModel')
Then you can get all your neighbourhoods sorted:
ThroughModel.objects.select_related().order_by('neighborhood__name')
This is untested.
Or, if you cannot change the database, just do a raw SQL join.