Using related_name in Django - django

I am getting confused with the usage of related_name in django models, i know that the idea is to give me access to all the fields of a different table with foreignkey i am just not sure how to use it.
My model.py:
class Component(MPTTModel):
name = models.CharField(max_length=100)
manufacturer = models.CharField(max_length=100)
model = models.CharField(max_length=100)
serial_number = models.CharField(max_length=255)
price = models.IntegerField()
note = models.TextField()
image = models.ImageField(blank=True, null=True,
upload_to='components_imgs')
parent = TreeForeignKey("self", verbose_name=(
"Parent Component"), blank=True, null=True, related_name='children', on_delete=models.CASCADE)
def __str__(self):
return f"{self.id}, {self.name}"
class Job(models.Model):
job_type = (
('I', 'Interval'),
('O', 'One time'),
)
name = models.CharField(max_length=100)
description = models.CharField(max_length=100)
type = models.CharField(max_length=1, choices=job_type)
interval = models.IntegerField()
is_critical = models.BooleanField()
due_date = models.DateField()
component = models.ForeignKey(
Component, related_name='jobs', on_delete=models.CASCADE)
runninghours = models.ForeignKey(
RunningHours, related_name="RHjobs", on_delete=models.CASCADE)
def __str__(self):
return self.name
my view.py:
def worklist(request):
components = Component.objects.all()
Component_jobs = components.jobs.all()
context = {"component":components,
"Component_jobs":Component_jobs}
return render(request,"worklist.html",context)
I am trying to understand why these lines give me an error 'TreeQuerySet' object has no attribute 'jobs'
components = Component.objects.all()
Component_jobs = components.jobs.all()
but these lines work just fine,
component = Component.objects.all()
component_id = Component.objects.get(id=pk)
job_id = component_id.jobs.all()
are they not the same but with one i am getting all the jobs for a specific ID and the other i am getting all the jobs for all components?
Edited,
My template :
<div class="worklist">
{%for component in component %}
<p> {{component.name}} </p>
<div class="runninghours-table-flex">
<table class="runninghours-table">
<thead>
<tr>
<th>Job name</th>
<th>Job type</th>
<th>Due Date </th>
<th>Interval</th>
<th>Rank</th>
<th>Execute Job</th>
</tr>
</thead>
<tbody>
{% for c_jobs in component_jobs%}
<tr>
<td>{{c_jobs.name}} </td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{%endfor%}
</tbody>
</table>
</div>
{%endfor%}
</div>

Assuming you want to display all components in a loop and then display all related jobs for each component in a nested loop, your template should look something like this simplified example
{% for component in components %}
{{ component }}
{% for job in component.jobs.all %}
{{ job }}
{% endfor %}
{% endfor %}
You should then use prefetch_related to fetch all related jobs for all components in a single query. There is no need to query the jobs in your view
components = Component.objects.all().prefetch_related('jobs')

As the error describes, this code returns a TreeQuerySet object.
components = Component.objects.all()
Trying to call fields like price will result in the same error.
components = Component.objects.all()
Component_jobs = components.price.all()
The reason this code works is because it is called on a specific Component instead of a collection of Components.
component = Component.objects.all()
component_id = Component.objects.get(id=pk)
job_id = component_id.jobs.all()
To get all jobs on a queryset, try using something like .values('jobs') from the queryset api reference
all_jobs = Component.objects.all().values('jobs')

Related

how to access 2 models attributes through foreignkey in django

Django learner here, I am trying to build a simple blog website in which i have created two models:
one is Post:
class Post(models.Model):
title = models.CharField(max_length=255)
author= models.ForeignKey(User, null=True, blank=True , on_delete=models.CASCADE)
article = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
slug = AutoSlugField(populate_from='title', unique=True, null=True, default=None)
category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.CASCADE )
def __str__(self):
return self.title
second is category:
class Category(models.Model):
categories = models.CharField(max_length=24, blank=True)
def __str__(self):
return self.categories
all i am trying to do is to show Category on home page, and when someone click on any category it will open up all the post related to that category.
This is home.html :
{% extends 'blog_pages/base.html' %}
{% block content %}
<div class = "container p-3">
<h3> This is your home page</h3>
</div>
<div class = "container p-1">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th scope="col">Categories</th>
<th scope="col">About</th>
</tr>
</thead>
<tbody>
{% for c in cat %}
<tr>
<th scope="row"><a href="{% url 'all_articles' c %}" ><p> {{c}}</P></a></th>
<td> How you can win in life</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
this is views.py :
def home(request):
cat = Category.objects.all()
return render(request, 'blog_pages/home.html',{'cat': cat})
def all_articles(request, c):
post = Post.objects.filter(category__contains = c).values()
return render(request,"blog_pages/all_articles.html",{'post':post})
I am getting this error " FieldError(
django.core.exceptions.FieldError: Related Field got invalid lookup: contains"
i have tried many possible ways to solve this problem, but nothing works.

Django initial loading of page taking too long

Hi I am a beginner at Django and I am working on a project that lists 100 companies in each page along with there contacts and also the amount of items sold. Here is an example:
As you can see the initial loading time of the page is very high. But when I refresh the page it refreshes very fast because I am using caching.
Here are some of my other files:
models.py
from __future__ import unicode_literals
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=150)
bic = models.CharField(max_length=150, blank=True)
def get_order_count(self):
orders = self.orders.count()
return orders
def get_order_sum(self):
orders = Order.objects.filter(company=self)
total_sum = sum([x.total for x in orders])
return total_sum
class Meta:
ordering = ['-id']
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150, blank=True)
email = models.EmailField()
def get_order_count(self):
orders = self.orders.count()
return orders
class Order(models.Model):
order_number = models.CharField(max_length=150)
company = models.ForeignKey(Company, related_name="orders", on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, related_name="orders", on_delete=models.SET_NULL, blank=True, null=True)
total = models.DecimalField(max_digits=18, decimal_places=9)
order_date = models.DateTimeField(null=True, blank=True)
added_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s" % self.order_number
views.py
from django.shortcuts import render
# Create your views here.
from django.views.generic import ListView
from mailer.models import Company, Contact, Order
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
The html
<div class="container">
<table class="table table-borderless">
{% if is_paginated %}
<tr><td>
{% if page_obj.has_previous %}
«
{% endif %}
</td>
<td></td>
<td></td>
<td>
{% if page_obj.has_next %}
»
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<th>Name</th>
<th>Order Count</th>
<th>Order Sum</th>
<th>Select</th>
</tr>
{% for company in company_list %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.get_order_count }}</td>
<td>{{ company.get_order_sum|floatformat:2 }}</td>
<td><input type="checkbox" name="select{{company.pk}}" id=""></td>
</tr>
{% for contact in company.contacts.all %}
<tr>
<td> </td>
<td>{{ contact.first_name }} {{ contact.last_name }}</td>
<td>Orders: {{ contact.get_order_count }}</td>
<td></td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
Is there any way in which I can reduce the initial load time. Please show me an efficient way to solve this problem.
Each {{company.get_order_count}} will hit the DB. Admittedly with a very simple query, but even so, it will slow things down.
You want to annotate the objects with this count. Use
from django.db.models import Count
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
def get_queryset(self):
return super().get_queryset().annotate( num_orders=Count('orders') )
and replace {{ company.get_order_count }} with {{ company.num_orders }}. This will turn N+1 DB queries into one DB query.
That's the easy one. There's a similar problem with get_order_sum which can almost certainly be solved with another annotation involving the django.db.Sum. Sorry but its late and my stomach is growling and I don't have any confidence that I would get that one right straight off the top of my head.
The cheat sheet on annotation is here. You might also need to look at aggregation.
Oh, and install Django_debug-toolbar in your developer environment. Every time in future it gets slow, you can just click there to see what SQL was executed and how long it took.

Can't combine different query sets and render them in django

So i have to render a table on html template which combines columns from 2 different base models. But the table doent seem to render well. Entries from other table are shown in different rows.
here is the screenshot of the table that has rendered
-I used the chain method from ittertools for this purpose. Not sure where I messed up.
Models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
image = models.ImageField(upload_to='profile_pics')
dob = models.DateField()
email = models.EmailField(max_length=50, unique=True)
class Placement(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
company = models.CharField(max_length=40)
position = models.CharField(max_length=40)
city = models.CharField( max_length=40)
bond = models.CharField( max_length=40)
ctc = models.CharField( max_length=40)
my Views.py:
def mentorintern(request):
table = Placement.objects.all()
name_student = Profile.objects.all()
in_table = list(chain(table, name_student))
return render(request, 'mentorinternship.html', {'in_table': in_table })
HTML template used to render the table:
<table class="table align-items-center table-flush table-hover" id="dataTableHover">
<thead class="thead-light">
<tr>
<th>Name of Student</th>
<th>Company</th>
<th>Position</th>
<th>City</th>
<th>CTC</th>
<th>Bond</th>
</tr>
</thead>
<tbody>
{% for std in in_table %}
<tr>
<td>{{std.first_name}}</td>
<td>{{std.company}}</td>
<td>{{std.position}}</td>
<td>{{std.city}}</td>
<td>{{std.ctc}}</td>
<td>{{std.bond}}</td>
</tr>
{% endfor %}
</tbody>
</table>
What you're trying to do can be achieved easier than combining two query sets. Since you have a OneToOneField of User, you can directly access user details like this: {{std.student.first_name}}} inside the for loop. Now if I understand what you want, you probably need a foreign key of Profile table in Placement table. This way you can access, the profile as {{std.profile.first_name}} and the user table as {{std.profile.user.first_name}}.

django how to model this table correctly?

Following the suggestions from my last post I got this far:
Post model:
class Post(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
Group model:
class Group(models.Model):
title = models.CharField(max_length=200)
url = models.URLField(unique=True)
contact_updated = models.DateField(auto_now=False, auto_now_add=True)
group_status = models.CharField(max_length=20)
admin = models.CharField(max_length=20)
admin_status = models.CharField(max_length=20)
frequency = models.IntegerField() # allowed post frequency
frq_scale = models.CharField(max_length=20, blank=True)
obs = models.TextField(blank=True)
posts = models.ManyToManyField(Post, through='control.Control')
Control model:
class Control(models.Model):
published = models.DateField(auto_now=False, auto_now_add=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE)
group = models.ForeignKey('groups.Group', on_delete=models.CASCADE)
This is control for posts in groups. I can have 1 post published in many groups controlled from Control model.
CORRECTION:
It is possible for a Post to be published in many groups.
How can I produce the table (link above) with those models? Or perhaps there is something I need to change?
The table I want to produce
class Control(models.Model):
published = models.DateField(auto_now=False, auto_now_add=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE)
group = models.ForeignKey('groups.Group', on_delete=models.CASCADE)
class Meta:
unique_together = (post, group )
I ended up creating a dictionary in the view to be passed to the template.
I haven't changed the models.
This is the view:
def control_list(request):
group_status = STATUS_LIST
group_query_idx = 1
period_initial = date.today()-timedelta(days=30)
period_final = date.today()
if request.method == "POST":
filter_form = FilterControl(request.POST)
if filter_form.is_valid():
group_query_idx = int(filter_form.cleaned_data['group_status'])
period_initial = filter_form.cleaned_data['period_initial']
period_final = filter_form.cleaned_data['period_final']
else:
filter_form = FilterControl()
if group_query_idx:
filtered_groups = Group.objects.filter_by_status(group_status[group_query_idx])
queryset_list = Control.objects.filter_by_group_status(group_status[group_query_idx])\
.filter(published__range=[period_initial, period_final])
query = request.GET.get("q")
if query:
queryset_list = queryset_list.filter(
Q(post__content__icontains=query) |
Q(post__title__icontains=query) |
Q(group__title__icontains=query) |
Q(group__admin__icontains=query) |
Q(group__obs__icontains=query)
).distinct() # avoid duplicated items
controls_per_group = {}
for group in filtered_groups:
control = queryset_list.filter(group_id=group.id)
controls_per_group[group.title] = control
context = {
"object_list": queryset,
"title": "Control",
"controls_per_group": controls_per_group,
"column": range(10),
"group_status": group_status,
"filter_form": filter_form,
}
return render(request, "control_list.html", context)
And this is the template:
<table class="table table-hover table-striped">
<thead class="thead-inverse">
<tr>
<th class="text-center">Action</th>
<th class="text-center">Group</th>
{% for value in column %}
<th class="text-center">#</th>
{% endfor %}
</tr>
</thead>
{% for key, value in controls_per_group.items %}
<tr>
<td class="text-center"><a class='btn btn-info btn-xs disabled' href="#"><i class="fa fa-pencil"></i></a>
<i class="fa fa-trash-o"></i></td>
<th class="text-center">{{ key }}</th>
{% for control in value %}
<th class="text-center">{{ control.published | date:"d/m/y" }}<br>{{ control.post.id }}</th>
{% endfor %}
</tr>
{% endfor %}

Filtering date within daterange and show data of one template to another template in django

I have created many sitting dates by using following Sitting model:
class Sitting(models.Model):
sit_date = models.DateField(blank=False,unique=True)
cut_off_date = models.DateField(null=True, blank=True)
ballot_date = models.DateField(null=True, blank=True)
sess_no = models.ForeignKey(Session,
on_delete=models.CASCADE)
genre = TreeForeignKey('Genre', null=True, blank=True, db_index=True, unique=True)
Output of sitting template as following format:
I also designed another model named Circular:
class Circular(models.Model):
cir_no = models.IntegerField(blank=False)
sit_start_date = models.DateField(blank=False)
sit_end_date = models.DateField(blank=False)
sess_no = models.ForeignKey(Session,
on_delete=models.CASCADE)
parl_no = models.ForeignKey(Parliament,
on_delete=models.CASCADE)
class Meta:
unique_together = ('cir_no', 'sess_no',)
Using Circular model I will create different circulars. Suppose I will create Circular no-1 for the period of 31 August to 30 September 2016. In this case When I will view circular no-1 it will only shows those sittings that are between above mentioned dates inclusive. And this circular will also contain the above mentioned Ministry/Division table. Should I include the sitting template in the circular template?
How can I do that. Any help will be much appreciated.
Edit:
Here is sitting_list.html template:
{% block content %}
<table cellpadding="10">
<tr>
<th>Sitting Day & Date</th>
<th>Ministry/Division</th>
</tr>
{% for sitting in sittings %}
<tr>
<td> {{ sitting.sit_date|date:"l, d F, Y" }}</td>
<td>
{% for genre in sitting.genre.get_descendants %}
{{ genre }},
{% endfor %}
<p>(Ballot: {{ sitting.ballot_date}})</p>
</td>
</tr>
{% endfor %}
</table>
{% endblock content %}
I'm not sure if i completely understand you right. But one way to get sittings for the period of a circular (without an explicit model relation) could look as this:
class Circular(models.Model):
# ...
sit_start_date = models.DateField(blank=False)
sit_end_date = models.DateField(blank=False)
# ...
#property
def sittings(self):
qs = Sitting.objects.filter(sit_date__gte=self.sit_start_date, sit_date__lte=self.sit_end_date)
return qs
Then you can use this propperty on a 'circular' instance:
c = Circular.objects.get(pk=1)
c.sittings