Django: join two tables - django

Here's what I currently have:
class Sessions(models.Model):
sessionId = models.AutoField(primary_key=True)
class Ip(models.Model):
ipId = models.AutoField(primary_key=True)
class Affiliation(models.Model):
affiliationId = models.AutoField(primary_key=True)
ip = models.ForeignKey("Ip", null=False, db_column="ipId")
session = models.ForeignKey("Sessions", null=False, db_column="sessionId")
Now I want to find the Sessions QuerySet which returns ipId=x. Basically this is what I'm trying:
Sessions.objects.filter(affiliation__ip=x)
Is there any way I can do this in django?

Found the answer!
psobjs = Affiliation.objects.filter(ipId=x)
queryset = Sessions.objects.filter(sessionId__in=psobjs.values('sessionId'))

Refactor your code
Apologize, Please bear with me and learn from it.
You don't have to explicitly specify the id in Django. unless your class is unmanaged. Django automatically use id or pk to refer to it.
class Sessions(models.Model):
pass
Model naming usually Singular Session.
class Session(models.Model):
pass
Watch out for conflict model name, Session is already used internally by Django django.contrib.sessions.models.Session. Use alternative name such as ClientSession would be better.
class ClientSession(models.Model):
pass
Use ManyToManyField (optional), it is just a helper to simplify your queryset.
class ClientSession(models.Model):
client_source = models.ManyToManyField('IP', through='Affiliation')
Use GenericIPAddressField
class IP(models.Model):
address = models.GenericIPAddressField()
You have no additional attribute in Affiliation, thus you can consider removing it.
class ClientSession(models.Model):
client_source = models.ManyToManyField('IP')
Final Model
class ClientSession(models.Model):
client_source = models.ManyToManyField('IP')
class IP(models.Model):
address = models.GenericIPAddressField()
Answer
Querying Session is very straight forward with the current Model.
x = 1 # Some ID
ClientSession.objects.filter(ips__id=x)

I think if you use a related_name in Affiliation model like this:
class Sessions(models.Model):
sessionId = models.AutoField(primary_key=True)
class Ip(models.Model):
ipId = models.AutoField(primary_key=True)
class Affiliation(models.Model):
affiliationId = models.AutoField(primary_key=True)
ip = models.ForeignKey("Ip", null=False, db_column="ipId")
session = models.ForeignKey(
"Sessions", null=False,
db_column="sessionId",
related_name="affiliation_session"
)
Now you can filter:
Sessions.objects.filter(affiliation_session__ip_id=X)
You can use select_related for optimization if necessary

I want to show a report where I want customer_name, mobile_no, from Details table and status from Status table. You can see my model
class Details(models.Model):
customer_name = models.CharField(max_length=100,unique=True)
mobile_no = models.IntegerField()
class Status(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=25)
purchase = models.ForeignKey(Details, null=True, on_delete=models.SET_NULL)
Solution
Step1: Add the following code to your view. [views.py]
def customer_report(request):
status_obj = Status.objects.all()
return render('reports/customer_report.html', {'status_obj':status_obj})
Step2: Add the following code at your template side [report.html]
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Mobile No</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for data in status_obj %}
<tr>
<td>{{data.purchase.customer_name}}</td>
<td>{{data.purchase.mobile_no}}</td>
<td>{{data.status}}</td>
</tr>
{% endfor %}
</tbody>
</table>

Related

How can I display something from data base after multiple criteria is satisfied in django

Here i have a Model Recommenders:
class Recommenders(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Recommender = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And another model Approvers:
class Approvers(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Approver = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And my SendApproval model as:
class SendApproval(models.Model):
Subject = models.CharField(max_length=256)
Date = models.DateField(null=True)
Attachment = models.FileField(upload_to=get_file_path)
SentBy = models.CharField(null=True, max_length=100)
Status = models.CharField(null= True, max_length=8, default="Pending")
Now my problem is that I have to display the Subject and Attachment from SendApproval table only when all the recommender's Status in Recommenders table related to that subject is "Approved"
Don't know how can I know that...Thanks in advance...
Actually not have any Idea about the solution but the best answer will be appreciated...By the way, I am new to StackOverflow...So please let me know if there is some ambiguity in my question.
Offer two ways.
1.Here the Recommenders model is filtered by the Status='Approved' field. By the Subject field, which is associated with the primary model, I get access to the SendApproval fields.
Replace bboard with the name of the folder where your templates are placed.
I have this: templates/bboard which are in the application folder.
views.py
def info(request):
recomm = Recommenders.objects.filter(Status='Approved')
return render(request, 'bboard/templ.html', {'context': recomm})
urls.py
urlpatterns = [
path('info/', info, name='info'),
]
templates
{% for a in context %}
<p>{{ 'Subject' }} : {{ a.Subject.Subject }} {{ 'Attachment' }} : Link
{{ 'id SendApproval' }} : {{ a.Subject.id }}</p>
{% endfor %}
2.You can also filter by primary model by fields from a secondary model that is linked to the primary. Here, I passed in the filter the name of the model in lower case and the search field.
In the primary model, by default, a property is created to refer to the secondary model, this is the name of the model in lower case and the _set prefix. In the outer loop, all received rows of the primary model are sorted out, and in the inner loop, through recommenders_set.all(), we get the Status. As you can see it is Approved.
sa = SendApproval.objects.filter(recommenders__Status='Approved')
print(sa)
for i in sa:
for k in i.recommenders_set.all():
print('status', k.Status)

Django can't display data from two models

I am trying to display data from two different models. The two models have a one-to-many relationship but I am not sure why it is not displaying the data from the MembersPresent model. Here are my models
and view
class fly_minute(models.Model):
mode = (
('Email', 'Email'),
('Zoom', 'Zoom'),
('Alternative', 'Alternative'),
)
mode_of_meeting = models.CharField(max_length=100, choices=mode, blank=False, )
date = models.DateField()
Time = models.TimeField()
minute_prepared_by = models.CharField(max_length=25)
location = models.CharField(max_length=25)
authorize_by = models.CharField(max_length=25)
item = models.TextField()
owner = models.CharField(max_length=25)
def __str__(self):
return self.mode_of_meeting
class MembersPresent(models.Model):
flyminute = models.ForeignKey(fly_minute, on_delete=models.CASCADE)
name = models.CharField(max_length=25)
status = models.CharField(max_length=25)
email = models.EmailField(max_length=25)
phone = models.IntegerField()
def __str__(self):
return self.name
#login_required(login_url='login_page')
def MinutesReport(request, minute_id):
report = fly_minute.objects.filter(id=minute_id)
return render(request, 'minute_report.html', locals())
{%for rpt in report%}
<tbody>
<tr class="table-active">
<td>{{rpt.flyminute.name}}</td>
<td>{{rpt.flyminute.status}}</td>
<td>{{rpt.flyminute.email}}</td>
<td>{{rpt.flyminute.phone}}</td>
</tbody>
{%endfor%}
You need to use 'prefetch_related()'
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
This has a similar purpose to select_related, in that both are designed to stop the deluge of database queries that is caused by accessing related objects, but the strategy is quite different.
models.py
class MembersPresent(models.Model):
flyminute = models.ForeignKey(
fly_minute,
related_name='memberspresent_set', # this will be the default name, incase of need of change provide a new one
on_delete=models.CASCADE
)
...
views.py
#login_required(login_url='login_page')
def MinutesReport(request, minute_id):
report = fly_minute.objects.filter(id=minute_id).prefetch_related('memberpresent_set')
return render(request, 'minute_report.html', locals())
For further control, you can use Prefetch along with prefetch_related()
Note:
locals() returns a dict of all the locally defined variables in the current scope and this may result in exposing sensitive info. Instead of that create a custom dict and return it.
...
context = {
'report': report,
...
}
return render(request, 'minute_report.html', context)
You are referencing and querying in the opposite way. Your fly_minute model doesn't contain MembersPresent model as the Foreign Key. You are filtering the fly_minute model and trying to access MembersPresent model. So, you should change your fly_minute to in this way:
class fly_minute(models.Model):
mode = (
('Email', 'Email'),
('Zoom', 'Zoom'),
('Alternative', 'Alternative'),
)
mode_of_meeting = models.CharField(max_length=100, choices=mode, blank=False, )
date = models.DateField()
Time = models.TimeField()
minute_prepared_by = models.CharField(max_length=25)
location = models.CharField(max_length=25)
authorize_by = models.CharField(max_length=25)
item = models.TextField()
owner = models.CharField(max_length=25)
member = models.ForeignKey(MembersPresent, on_delete=models.CASCADE)#add this line and remove fly_minute in MembersPresent model
def __str__(self):
return self.mode_of_meeting
Then in template file change it to in this way:
{%for rpt in report%}
<tbody>
<tr class="table-active">
<td>{{rpt.member.name}}</td>
<td>{{rpt.member.status}}</td>
<td>{{rpt.member.email}}</td>
<td>{{rpt.member.phone}}</td>
</tbody>
{%endfor%}
I guess it works
Thanks, I ended up using fetch-related and had to make adjustments to my template.
<tr>
<th>Name</th>
<th>Status</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
{%for rpt in report%}
{%for rpts in rpt.memberspresent_set.all%}
<tbody>
<tr class="table-active">
<td>{{rpts.name}}</td>
<td>{{rpts.status}}</td>
<td>{{rpts.email}}</td>
<td>{{rpts.phone}}</td>
</tbody>
{%endfor%}
{%endfor%}

quickly access models attributes via foreign key relationship in django template

I have a model where one field is a ForeignKey so that each child object is linked to a parent object.
In my (jinja2) templates, I list some attributes from a subset of objects from the child model, including one of the parents' attributes. The page loads very slowly, so I am wondering if there is a faster way to do the following:
views.py
class TransactionView(LoginRequiredMixin, ListView):
model = Transactions
context_object_name = 'transaction_list'
template_name = 'bank/transactions.html'
def get_queryset(self):
return Transactions.objects.filter(owner_id=self.request.user)
template.html
<tbody>
{% for transaction in transaction_list %}
<tr>
<td>{{transaction.source_document.service_provider}}</td>
<td>{{transaction.account}}</td>
<td>{{transaction.tnsx_date}}</td>
<td>{{transaction.end_bal}}</td>
<td>{{transaction.amount}}</td>
<td>{{transaction.category}}</td>
</tr>
{% endfor %}
</tbody>
models.py
class Transactions(models.Model):
def __str__(self):
return str(self.tnsx_uuid)
owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
db_index=True,
editable=True,
)
source_document = models.ForeignKey(
Document,
on_delete=models.CASCADE,
editable=True,
)
tnsx_uuid = models.UUIDField(default=uuid.uuid4, unique=True)
account = IBANField(enforce_database_constraint=True)
currency = models.CharField(max_length=4, blank=False, null=False)
currency_assumed = models.BooleanField(null=False)
<etc>
As iklinac suggested, using .prefetch_related() increases the speed of the query dramatically.
change views.py to the following:
class TransactionView(LoginRequiredMixin, ListView):
model = Transactions
context_object_name = 'transaction_list'
template_name = 'bank/transactions.html'
def get_queryset(self):
return Transactions.objects.filter(owner_id=self.request.user).prefetch_related('source_document')

Django printing many to many children not in same order as input

I have a Tourguide model with a many to many relationship with Places. Both models are defined as :
class Tourguide(models.Model):
id = models.AutoField(db_column='ID', primary_key=True, unique=True)
title = models.CharField(db_column='Title', max_length=255, blank=True)
places = models.ManyToManyField(Place, db_column='placesdj')
places_app = models.CharField(max_length=255, db_column='places')
created_on = models.DateTimeField(db_column='Created_On', default = now)
class Meta:
managed = False
db_table = 'tourguide'
def __str__(self):
return self.title
class Place(models.Model):
place_id = models.AutoField(db_column='ID', primary_key=True)
place_name = models.CharField(db_column='Place_Name', max_length=255)
address_line_1 = models.CharField(db_column='Address_Line_1', max_length=255)
address_line_2 = models.CharField(db_column='Address_Line_2', max_length=255)
area = models.CharField(db_column='Area', max_length=255)
class Meta:
managed = False
db_table = 'Place'
def __str__(self):
return self.place_name
THE PROBLEM
When I try to print the places in a tourguide using :
{% for place in tour.places.all %}
<tbody>
<td>{{ forloop.counter }}</td>
<td>id: {{place.place_id}}, {{ place.place_name }} </td>
<td> {{ place.description }} </td>
</tbody>
{% endfor %}
The order of the places is all random and not the same as the order I inputed it as. I want to print the places in the same order that I placed them in. My view for listing the tourguides and places within them is as so.
def tour_list(request, template_name='tourguides/tour_list.html'):
tourguide_list = Tourguide.objects.all()
paginator = Paginator(tourguide_list,6)
page = request.GET.get('page')
tourguides = paginator.get_page(page)
data = {}
data['tourguides'] = tourguides
return render(request, template_name, data)
Update
I have an array of place id's in the tourguide table, is there a way i can use that?
Relational databases do not sort rows by default, and since they are internally stored in all kinds of weird data structures, there is no "input order" either.
As a workaround, you can use the automatically generated place_id field as a sort key as it is pretty much guaranteed to go up as new entries are created. You can add a default sorting to your Place class, for example:
class Place(models.Model):
...
class Meta:
...
ordering = ('place_id',)
That would guarantee that any queries that will return a Place queryset will be ordered by place_id by default (i.e. unless you have an explicit .order_by() clause).

Accessing last row from table C that has FK from table B, which is in one-to-one relationship with A

Let's say we have 3 models:
class A(models.model):
name = models.CharField(max_length=30, unique=True)
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
class C(models.model):
name = models.CharField(max_length=30, unique=True)
b = models.ForeingKey(B, on_delete=models.CASCADE)
transaction_count = models.IntegerField(default=0)
and one view:
class AListView(generic.ListView):
model = A
In that view (template) I need to show: name of A, name of B and the last row (ordered by date) of "transactioncount" for each repository from b.
In my template I iterate over items in A and show them in following way:
{% for a in A %}
<tr>
<td>{{a.name}}</td>
<td>{{a.b.name}}</td>
<td>{{??? Don't know what to put here, to show the last row. I tried: a.b.c|last}}</td>
</tr>
{% endfor %}
I tried to build custom tags and use template functions like, but that unfortunately doesn't work:
{% with a.b.c_set.all|last as val %}
<td>val</td>
{% endwith}
Among my other tries would be to build a new queryset, but then I don't know how to assign items from model A to that queryset. I tried:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'A': A.objects.all(),
'c_data': C.objects.order_by('B', '-date').distinct(
'B')
})
)
What would be the best "pythonic" way to do this?
Thanks
First, of all, don't forget to set the back relation name like:
b = models.ForeingKey(B, on_delete=models.CASCADE, related_name='all_c')
Then:
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
#property
def last_c(self):
if self.all_c.exists():
return self.all_c.order_by('-date').last()
In your jinja template just write:
a.b.last_c
Have fun!