django: How to add extra field to all items in queryset - django

My models looks this way
class Test(models.Model):
name = models.CharField(max_length=255)
test_plan = models.FK(TestPlan)
class TestPlan(models.Model):
name = models.CharField(max_length=255)
class Result(models.Model):
user = models.FK(User)
test_plan = models.FK(TestPlan)
test = models.FK(Test)
result = models.Boolean()
In the case I have a plan object in my view, I can access all tests by doing plan.test_set.all(). But is there a way to append all existing results to that query (so a result object is attached to a given test case, if it exists).
=====
I am trying to run the following
def get(self, request, *args, **kwargs):
task = get_object_or_404(Plan, id=kwargs.get('id'))
return render(request, self.template_name,
{"task": task})
def post(self, request, *args, **kwargs):
data = request.POST
task = get_object_or_404(Task, id=kwargs.get('id'))
tc = get_object_or_404(Test, id=data.get('tc_id'))
tc_result, _ = Result.objects\
.get_or_create(task=task, test=tc,
result=data.get('tc_result_status'))
return HttpResponse("OK")
And in template:
{% for test in task.tests_set.all %}
{{ test }}(here I want to render test result, if it I have one for given test in given plan, for current user)
{% endfor %}

You're looking to access a reverse one to many relationship? Just iterate in the same way you accessed tests_set.
{% for test in task.tests_set.all %}
{{ test }}
{% for result in test.result_set.all %}
{{ result }}
{% endfor %}
{% endfor %}

To filter the results in task.tests_set.all() or test.result_set.all() you need to use prefetch_related with custom Prefetch object.
Something like:
task = get_object_or_404(
TestPlan.objects.prefetch_related(
Prefetch('test_set', queryset=Test.objects.prefetch_related(
Prefetch('result_set', queryset=Result.objects.filter(user=request.user))
))
),
id=kwargs.get('id')
)
And in template:
{% for test in task.test_set.all %}
{% for result in test.result_set.all %}
{{ result }}
{% endfor %}
{% endfor %}

Related

multiple query model in same view django

I've been doing some django these past weeks and I'm having a problem.
I have a page where Articles are shown. No problem while reovering all articles from db. But now I'd like to get all categories (an Article has a category) that I have in my database.
So I can display like this in my page:
List of categories
-cat1
-cat2
-cat3
List of articles
-art1
-art2
-art3
But I don't know how to do with both queries.
Here's what I've tried.
class IndexView(generic.ListView):
template_name = 'eduardoApp/index.html'
context_object_name = 'article_list'
def get_queryset(self):
return Article.objects.order_by('article_name')
def get_categories(request):
category_list=Category.objects.all()
context = {'category_list':category_list}
return render(request,'eduardoApp/index.html',context)
And in my view:
<h2>List of categories</h2>
{% if category_list %}
{% for category in category_list %}
<p>{{ category.name }}</p>
{% endfor %}
{% else %}
<p>no categorys</p>
{% endif %}
<h2>List of articles</h2>
{% if article_list %}
<div class="flex-container">
{% for article in article_list %}
<div>{{ article.article_name }}</div>
{% endfor %}
</div>
{% else %}
<p>No articles...</p>
{% endif %}
{% endblock %}
In my view I keep seeing no categorys displayed (since category_list does not exist but don't know why and how to fix)
ListView is creating context with 'objects' as queryset get_queryset returns.
I suppose your custom method get_categories hasn't been used anywhere?
Best practice here is to override get_context_data method like...
class IndexView(generic.ListView):
...
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['category_list'] = ...
return context

Is there a better way to get and display data from a related object aside from creating a dictionary?

I have two models with one having a foreign key to the other as such:
Models:
class WhoAmI(models.Model):
name = models.CharField(max_length=200)
company = models.CharField(max_length=200)
def __str__(self):
return self.name
class SolarClient(models.Model):
name = models.CharField(max_length=200)
client_owner = models.ForeignKey(WhoAmI, on_delete=models.CASCADE, related_name='solarclients')
addr = models.CharField(max_length=200)
city = models.CharField(max_length=200)
state = models.CharField(max_length=200)
email = models.EmailField()
I am trying to simply display an html table showing each client a salesperson has, with the salesperson listed first with a table of clients below their name.
The only way I could figure out how to do this was to create a dictionary using the code shown below.
class Homeowners(DetailView):
def get(self, request, **kwargs):
salespersons = WhoAmI.objects.all()
homeowners = SolarClient.objects.all().order_by("client_owner") #the name 'objects' is the Manager
rangers = {}
for e in salespersons:
k = salespersons.get(id = e.id)
v = k.solarclients.all()
rangers[k] = v
return render(request, 'homeowners.html', {'homeowners': homeowners, 'salespersons': salespersons, 'rangers': rangers })
I then iterate over the dictionary using:
{% for key, values in rangers.items %}
... display salesperson
{% if values %}
{% for v in values %}
.... display clients
{% endfor %}
{% else %}
... display "No Clients"
{% endif %}
{% endfor %}
Is there a more efficient way to do this? It seems silly to put the data into a dictionary to display it, but after many, many hours of trying different methods, this is the only way I could display the data.
thanks for any suggestions.
views.py
class Homeowners(DetailView):
def get(self, request, **kwargs):
salespersons = WhoAmI.objects.all()
return render(request, 'homeowners.html', {'salespersons': salespersons })
html:
{% for sales in salespersons %}
{% for client in sales.solarclients.all %}
------ Print Client
{% empty %}
---- Client not exist
{% endfor %}
{% endfor %}
There is a nice handy template filter called regroup built in Django which does exactly what you're looking for.
# views.py
homeowners = SolarClient.objects.all().order_by("client_owner").select_related('client_owner')
return render(request, 'homeowners.html', {'homeowners': homeowners})
# homeowners.html
{% regroup homeowners by client_owner as home_owners_list %}
<ul>
{% for client_owner in home_owners_list %}
<b>{{ client_owner.grouper }}</b>
<ul>
{% for client in client_owner.list %}
<li>{{ client.name }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
The select_related method is just for performance improvement and omitting it wouldn't affect functionality.

Combining model data to form url pattern

I am generating a generic list right now, with the following code:
views.py
class ServiceReportIndex(LoginRequiredMixin, ListView):
model = TblServiceRecords
context_object_name = 'all_servicereports'
login_url = 'login'
template_name = 'servicereport/servicereport_index.html'
def get_context_data(self, **kwargs):
context = super(ServiceReportIndex, self).get_context_data(**kwargs)
context['companies'] = TblCompanies.objects.all()
return context
In my template, I want to generate a URL using both of the models. The TblServiceRecords model contains a column that references the company_id, which is the primary key of the appropriate company in the TblCompanies model. I want to use the company_name from the Companies model in my list view. How would I go about doing that? I'm sure it's simple but I can't seem to get my url tags done correctly.
<div class="col-sm-4">
<p>Company Name</p>
{% for servicereport in all_servicereports %}
<p>{% for servicereport.company_id in companies.company_id %} {{ companies.company_name }} {% endfor %}</p>
{% endfor %}
</div>
Also, how can I be sure my views.py is set up correctly for multiple model functionality? I ask because if I put
{% for company_name in companies %}
{{companies.company_name}}
{% endfor %}
In my template, nothing comes up, but there are no errors either.
Probably you cannot see companies bacause of this:
{{companies.company_name}}
companies is queryset and it does not have company_name property.
Try this:
{% for company_name in companies %}
{{company_name.company_name}}
{% endfor %}

Need some advice for duplicated queries

I have a lot of duplicated queries (in django debug toolbar) when i load my menu tabs, im sure i can optimize this but don't find the good way.
Models :
class Categorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
def __str__(self):
return self.nom
def getscateg(self):
return self.souscategorie_set.all().filter(visible = True)
class SousCategorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
categorie = models.ForeignKey('Categorie')
def __str__(self):
return self.name
def gettheme(self):
return self.theme_set.all().filter(visible = True)
class Theme(models.Model):
name = models.CharField(max_length=100)
visible = models.BooleanField(default = False)
souscategorie = models.ForeignKey('SousCategorie')
def __str__(self):
return self.name
Views :
def page(request):
categs = Categorie.objects.filter(visible = True)
return render(request, 'page.html', locals())
Templates :
{% for categ in categs %}
<li>
{{categ.name}}
<ul>
{% for scateg in categ.getscateg %}
<li>
{{scateg.name}}
<ul>
{% for theme in scateg.gettheme %}
<li>{{ theme.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
I have look at prefetch_related but only work if i want load Categorie from SousCategorie and SousCategorie from Theme, so if i understand i need the contrary of this...
Solved !
If it can help :
from .models import Categorie, SousCategorie, Theme, SousTheme
from django.db.models import Prefetch
pf_souscategorie = Prefetch('souscategorie_set', SousCategorie.objects.filter(visible=True))
pf_theme = Prefetch('souscategorie_set__theme_set', Theme.objects.filter(visible=True))
pf_soustheme = Prefetch('souscategorie_set__theme_set__soustheme_set', SousTheme.objects.filter(visible=True))
categs = Categorie.objects.filter(visible=True).prefetch_related(pf_souscategorie, pf_theme, pf_soustheme)
and call like this in the template :
{% with currscat=categ.souscategorie_set.all %}
{% with currth=souscateg.theme_set.all %}
{% with currsth=theme.soustheme_set.all %}
Bye
In Django every time you evaluate a new queryset a query is executed, so you need to reduce the number of queryset being used. Here is what is happening:
You create a queryset Categorie.objects.filter(visible=True) and it is passed into the view layer, there the first query is executed at the this tag {% for categ in categs %}
Inside the loop, for every category you're calling a method categ.getscateg which returns a new queryset return self.souscategorie_set.all().filter(visible = True), this queryset will be executed at the second loop in your template {% for scateg in categ.getscateg %}
The same thing happens with {% for theme in scateg.gettheme %}
Using prefetch_related was the right move, try something like (didn't test it):
Categorie.objects.filter(visible=True, souscategorie_set__visible=True, souscategorie_set__theme_set__visible=True).prefetch_related('souscategorie_set__theme_set')
prefetch_related works by running a first query to load the categories that satisfy your current filter, then it executed a second query to load all subcategories and so on.
In other cases you can use select_related, but that only works when a single query can be used, as an example, it would work if you needed the category and subcategory of a theme, like:
Theme.objects.filter(pk=1).select_related('souscategorie__categorie')
The difference here is that Theme is the one that has the ForeignKey, so it has only one subcategory, and it can be loaded with a single join, that means you can use select_related only when your queryset is from the Model that points and I think that it also works with OneToOneField.
I have reduce my querysets by 2 by using "with"
It's a good point but i always got a lot of duplicate (like 44 duplicate for 51 queries), for example i hit my database when i do that :
{% for categ in categs %}
{% with currscat=categ.getscateg %}
<li class = "{% if currscat %} has_menu {% endif %}"> {{categ.name}} # This line hit database
{% if currscat %} # This line hit database
<ul class = "menu-1"> ... </ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
I try to use prefetch_related like this :
categs = Categorie.objects.filter(visible=True).prefetch_related('souscategorie_set')
but thats just add query to database, don't reduce...
Some advice?
Thanks

Django getting list of records (with FK) related to object -not rendering template correctly

I am trying to follow this previous question here:
Django: getting the list of related records for a list of objects
but can't seem to get it to work.
I get a list of owners but do not get a list of pet names. The html code doesn't seem to execute the 2nd FOR loop. Any ideas?
models.py
class Teacher(models.Model):
teacher = models.CharField(max_length=300, verbose_name="teacher")
def __unicode__(self):
return self.teacher
class Owner(models.Model):
relevantteacher = models.ForeignKey(Teacher, verbose_name="teacher")
owner = models.CharField(max_length=300, verbose_name="owner")
def __unicode__(self):
return self.owner
class PetName(models.Model):
relevantowner = models.ForeignKey(Owner, verbose_name="owner")
pet_name = models.CharField(max_length=50, verbose_name="pet Name")
def __unicode__(self):
return self.pet_name
views.py
def ownersandteachers(request):
owners = Owner.objects.all()
context = {'owners': owners}
return render(request, 'ownersandpets.html', context)
template
{% for i in owners %}
{{ i.owner }} has pets:<br />
{% for v in owners.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endfor %}
You are doing
{% for v in owners.petname_set.all %}
where you should be doing
{% for v in i.petname_set.all %}
owners is the queryset, but you need to get the petname_set for the individual object in the queryset which is i in this case.
Also, I would recommend a condition check if i.petname_set exists. If not, do not show the has pets: text.
{% for i in owners %}
{{ i.owner }}
{% if i.petname_set.count %} has pets:<br />
{% for v in i.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endif %}
{% endfor %}