Django - Problem with Model Manager - Query - python

I'm still a beginner and I'm stuck in a challenging spot for me.
I can't get data from a foreign key table to be inserted "correctly" into a column of a ListView.
I basically want to create a list view of a table (FeatureFilm). This also works. But in one column I get information from another table and here I get data, but not the one that belongs to the particular table row.
Here are my models. The table I want to show is "FeatureFilm" model. This model is inherited from my base Project class "ProjectBaseModel".
Then there is another table "CompanyInvolved Model". This is attached to the FeatureFilm table with a foreign key (feature_id).
So movies are stored (FeatureFilm) and different companies are involved in the creation process of the movies. (CompanyInvolved)
class ProjectBaseModel(models.Model):
title = models.CharField("Titel", max_length=100, blank=False, unique=True)
leading_postproduction_id = models.ForeignKey(
Company,
verbose_name="Federführende Postproduktion",
on_delete=models.SET_NULL,
blank=True,
null=True,
)
phase = models.CharField(choices=post_phase, max_length=30, blank=True, null=True)
former_title = models.CharField("Titel, ehemalig", max_length=100, blank=True)
title_international = models.CharField(
"Titel, international", max_length=100, blank=True, null=True, unique=True
)
class FeatureFilm(ProjectBaseModel):
class Meta:
verbose_name = "Kinofilm"
verbose_name_plural = "Kinofilme"
ordering = ["title"]
class ProductionManager(models.Manager):
def get_production(self):
return (
super()
.get_queryset()
.filter(company_role="Produktion", is_production_list=True)
.values_list("company_involved__name")
)
class CompanyInvolved(models.Model):
feature_id = models.ForeignKey(
FeatureFilm,
on_delete=models.CASCADE,
null=True,
blank=True,
)
tv_movie_id = models.ForeignKey(
TvMovie, on_delete=models.CASCADE, null=True, blank=True
)
company_role = models.CharField(
choices=company_role,
max_length=15,
blank=True,
help_text="Produktion, Co-Produktion, Kinoverleiher, Sender, Weltvertrieb",
)
company_involved = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
is_production_list = models.BooleanField(
default=False,
verbose_name="Produktion in Liste",
)
productionmanager = ProductionManager()
def __str__(self):
return "#" + str(self.pk)
class Meta:
verbose_name = "Produktion, Co-Produktion, Verleih, Sender, Weltvertrieb"
verbose_name_plural = "Produktion, Co-Produktion, Verleih, Sender, Weltvertrieb"
ordering = ["pk"]
I basically wanted to generate the output now via the template. I can iterate the lines with the template for loop. But I also learned that more complex queries don't work in the DjangoTemplate language, or simply don't belong there.
I don't need every row of data from the CompanyInvolved, but only the company_role = "Production" and is_production_list = True.
A combined "Where" clause in the template now nice, but doesn't exist, so I built myself a MODEL MANAGER (ProductionManager) that does this filtering in the model.
here is the View:
class FeatureListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
permission_required = "project.can_access_featurefilm_list"
model = FeatureFilm
template_name = "project/feature-list.html"
def handle_no_permission(self):
return redirect("access-denied")
ans here is the depending snippet in the template:
<tbody>
{% for project in object_list %}
<tr>
<td>{{ project.title }}</td>
<td>{{ project.companyinvolved_set.get_production }}
<br>
</td>
<td>{% if project.program_length_planned %}
{{ project.program_length_planned }}
{% endif %}
</td>
<td>{{ project.global_shooting_resolution }}</td>
<td>{{ project.global_resolution_theatrical }}</td>
<td>{% if project.hdr == 1%}
ja
{% else %}
nein
{% endif %}
</td>
<td>{{ project.stafflist.postproduction_supervisor_id.username }}</td>
<td>{% if project.phase %}
{{ project.phase }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
so I iterate over each movie project with {% for project in object_list %} and then I want to show in my "problem" column the company that has the role production and since there can be more than one, the one that was previously marked by the user for the lists view - > is_production_list = TRue and then the output should come: {{ project.companyinvolved_set.get_production }}.
The result is going in the right direction, but it is still not finally correct. I get the CompanyINvolved data and these are also filtered by company_role = "Production" and is_production_list = True , but these values are now displayed to me each time in each individual Row the same, I get so not per ROW the associated productions but simply each time ALL. I'm missing the reference to the FeatureFilm object, but I don't know how to get this now, or where to put this reference now?
changed my template:
<tbody>
{% for project in object_list %}
<tr>
<td>{{ project.title }}</td>
<td>
!! {{ project.production_companies.company_involved.name}}!!
<br>
</td>
<td>{% if project.program_length_planned %}
{{ project.program_length_planned }}
{% endif %}
</td>
<td>{{ project.global_shooting_resolution }}</td>
<td>{{ project.global_resolution_theatrical }}</td>
<td>{% if project.hdr == 1%}
ja
{% else %}
nein
{% endif %}
</td>
<td>{{ project.stafflist.postproduction_supervisor_id.username }}</td>
<td>{% if project.phase %}
{{ project.phase }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>

I think your code is not working because get_production calls super().get_queryset(), which returns a new QuerySet, without any filters applied, not based on the Queryset in the reverse look up in companyinvolved_set
Your best bet is to add a #property annotation to access the filtered list through your FeatureFilm model
#property
def production_companies(self):
return [company for company in self.companyinvolved_set.all() if company.company_role == 'Produktion' and company.is_production_list]
Then add this to your template:
<td>{% for production in project.production_companies %}{{production.company_involved.name}}{% endfor %}</td>
And to prevent a lot of queries, change the get_queryset method in your view to this:
def get_queryset(self):
queryset = FeatureFilm.objects.prefetch_related('companyinvolved_set').all()
return queryset

Related

Django: Add result from second queryset to first queryset

I have two models from two different databases (one read-only) without ForeignKey between the two models (did not get that working, as far i found it isn't possible).
In the main model I store the ID from the second model (read-only DB).
I want to display multiple records/rows on one view (like al table)
There for I want to get the content of the second model with the id from the main model.
and combine it to one row.
Normal you can get it by the ForeignKey but did won't work with 2 different databases.
What i got (simplified):
model.py
class Overeenkomst(models.Model):
park = models.IntegerField(blank=False, null=False, default='0')
object = models.IntegerField(blank=False, null=False, default='0') # ID from model second database
date_start = models.DateField()
date_end = models.DateField()
class Object(models.Model):
id = models.IntegerField(db_column='ID', primary_key=True) # Field name made lowercase.
nummer = models.IntegerField(db_column='NUMMER', blank=True, null=True) # Field name made lowercase.
omschrijving = models.CharField(db_column='OMSCHRIJVING', max_length=50, blank=True, null=True) # Field name made lowercase.
idobjectsoort = models.IntegerField(db_column='IDOBJECTSOORT', blank=True, null=True) # Field name made lowercase.
idobjecttype = models.IntegerField(db_column='IDOBJECTTYPE', blank=True, null=True) # Field name made lowercase.
(.....)
class Meta:
managed = False
db_table = 'OBJECT'
unique_together = (('nummer', 'idpark', 'id'), ('id', 'idpark', 'idobjecttype', 'idobjectsoort', 'dubbelboeking'), ('code', 'id'),)
def __str__(self):
return self.omschrijving
view.py
def ovk_overview(request):
ctx={}
overeenkomsten =models.Overeenkomst.objects.filter(park=request.session['park_id'])
ovk = []
for overeenkomst in overeenkomsten:
obj = models.Object.objects.using('database2').filter(pk=overeenkomst.object).values('omschrijving')
##### Here I Missing a part #####
ctx['overeenkomsten'] = ovk
return render(request, 'overeenkomsten/overzicht.html', context=ctx)
overzicht.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<table class='table table-sm'>
<tr>
<th>#</th>
<th>Object</th>
<th>Start datum</th>
<th>Eind datum</th>
<th> </th>
</tr>
{% for ovk in overeenkomsten %}
{{ ovk }}::
<tr>
<td>{% now 'Y' %}{{ ovk.park }}{{ovk.object}}{{ovk.id}}</td>
<td>{{ ovk.object.omschrijving}}</td>
<td>{{ ovk.date_start|date:"d-m-Y" }}</td>
<td>{{ ovk.date_end|date:"d-m-Y" }}</td>
<td><button class="btn btn-primary">Download pdf</button></td>
</tr>
{% endfor %}
</table>
{% endblock %}
I have tried to use list() and chain() (as answer https://stackoverflow.com/a/8171434) but then i get only the values of the Object model and nothing from the Overeenkomsten model.
I hope someone has a answer/idea for me.
You could do something like this.
views.py
from django.forms.models import model_to_dict
def ovk_overview(request):
ctx={}
overeenkomsten =models.Overeenkomst.objects.filter(park=request.session['park_id'])
ovk = []
for overeenkomst in overeenkomsten:
overeenkomst_dict = model_to_dict(overeenkomst)
obj = models.Object.objects.using('database2').get(pk=overeenkomst.object) #Assumed there is always an obj. If not, change accordingly.
overeenkomst_dict['omschrijving'] = obj.omschrijving
ovk.append(overeenkomst_dict)
ctx['overeenkomsten'] = ovk
return render(request, 'overeenkomsten/overzicht.html', context=ctx)
overzicht.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<table class='table table-sm'>
<tr>
<th>#</th>
<th>Object</th>
<th>Start datum</th>
<th>Eind datum</th>
<th> </th>
</tr>
{% for ovk in overeenkomsten %}
{{ ovk }}::
<tr>
<td>{% now 'Y' %}{{ ovk.park }}{{ovk.object}}{{ovk.id}}</td>
<td>{{ ovk.omschrijving }}</td>
<td>{{ ovk.date_start|date:"d-m-Y" }}</td>
<td>{{ ovk.date_end|date:"d-m-Y" }}</td>
<td><button class="btn btn-primary">Download pdf</button></td>
</tr>
{% endfor %}
</table>
{% endblock %}
First, a warning. Don't call fields object or id. They aren't Python reserved words, but using them overlays the meanings normally supplied by Python. It's confusing as hell, and in the case of object in particular, is also likely to cause you a world of pain at a later date if you start using Class-based views and Mixins. So call them something_id, or just obj.
OK. An idea ... yes. It depends on having enough memory to convert the main queryset into a list of objects. Then you "annotate" the objects in the first list, with the data from the corresponding objects in the second database.
I've replaced object with other_id in the following, because I simply couldn't think about it with the original name. It was like BLUE
printed in RED ink.
# query the first DB. You might want to chheck that the length
# of the query is sensible, and/or slice it with a maximum length
overeenkomsten = models.Overeenkomst.objects.filter(park=request.session['park_id'])
if overeenkomsten.count() > MAX_OBJECTS: # shouldn't ever happen
# do something to save our server!
overeenkomsten = overeenkomsten[:MAX_OBJECTS]
# fetch all the data
overeenkomsten = list( overeenkomsten )
# get the required data from the other DB.
# One query, retaining pk to tie the two together.
# avoids N queries on second DB
other_db_ids = [ x.other_id for x in overeenkomsten ] # was x.object
data_from_other_db = models.Object.objects.using('database2'
).filter(pk__in = other_db_ids
).values_list('pk', 'omschrijving'
)
# convert to a dict. This way for clarity and because I can't remember the dict method
#for converting a list of key/value pairs into a dict.
omschrivings = {}
for k,v in data_from_other_db:
omschrivings[k] = v
# "Annotate" the objects from the first query with the data from the second.
# It's read-only so no need to worry about saving it should somebody update it.
# (but you could subclass the save method if it wasn't)
for obj in overeenkomsten:
setattr( obj, 'omschriving', omschrivings[ obj[x.other_id] ] )
And in the template, just
<td>{{ ovk.omschrijving}}</td>

Django Using Variable in Filter within For Loop

I am trying to get the child object/field value using a parent object. The parent is a variable within a for loop and I can't seem to hand it into the custom tag.
#custom_tags.py
#register.simple_tag()
def assigned_to(sample):
#sample=sample.pk
return Lab_Request.objects.filter(sample=sample).first().lab.lab_name
#register.filter()
def assigned_too(sample):
#sample=sample.pk
return Lab_Request.objects.filter(sample=sample).first().lab.lab_name
#sample.html
{% for sample in samples %}
{% static sample.0|assigned_too %}
{% if user.profile.employee.pk == sample.inspector.employee.pk or perms.ics.view_sample %}
<tr>
<td class="column1">{{ sample.sample_date }}</td>
<td class="column2">{{ sample.status|sample_status_options }}</td>
<td class="column3">{{ sample.sample_number }}</td>
<td class="column4">{{ sample.order.customer.customer_name }}</td>
<td class="column5">{{ sample.lab_request.placeholder_to_be_replaced }}{{ sample.lab_request.lab.lab_name }}{{ sample.inspection_request.inspector.employee.employee_first_name }}</td>
<td class="column6">{{ sample.date_modified|date:'M. d, Y' }}</td>
</tr>
{% endif %}
{% endfor %}
{% static sample|assigned_too %} is the part I am struggling with. I have also tried to write a function and call it like {% assigned_to {{ sample }} %}. It does work if I use {% static 1|assigned_too %} but then it doesn't iterate with my loop like it needs to. I'm not sure if I am doing this this most complicated way possible. I just want information from a child of the parent such as {{ sample.lab_request.lab.lab_name }} where sample is a parent object and lab_request is a child model.
EDIT:
#views.py
class SampleHomeView(ListView):
model = Sample
samples = Sample.objects.all
context_object_name = 'samples'
template_name = 'ics/sample.html'
ordering = ['-sample_date']
paginate_by = 10
#urls.py
path('sample/', SampleHomeView.as_view(), name='sample-home'),
#models.py
class Lab_Request(models.Model):
#Add stuff here
placeholder_to_be_replaced = models.CharField(max_length=1)
lab = models.ForeignKey(Lab, on_delete=models.CASCADE, null=True, blank=True)
sample = models.ForeignKey(Sample, on_delete=models.CASCADE)
class Sample(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
status = models.CharField(max_length=2, choices=SAMPLE_STATUS_OPTIONS, default="01")
class Order(models.Model):
order_number = models.CharField(max_length=19, unique=True, editable=False, default=get_order_number)
status = models.CharField(max_length=2, choices=ORDER_STATUS_OPTIONS)
specification = models.ForeignKey(Specification, on_delete=models.CASCADE, null=True, blank=True) #Needs work to determine which spec is appropriate
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Lab(models.Model):
status = models.CharField(max_length=2, choices=STATUS_OPTIONS)
lab_name = models.TextField(max_length=100, unique=True)
OK I figured it out. I needed to call the child to the parent slightly differently than I was doing. I need to use {{ sample.lab_request_set.all.0.lab }} instead of the {{ sample.lab_request.lab }}
For anyone that needs this in the future, if you do not set a related name in the ForeignKey then it will be parent_model_name.child_model_name_set.all this will give you a query set that you can then iterate through or just select the first like I did with the .0 at the end of the .all. This works in a template. If you need it in a python file then you will be calling the function with parent_model_name.child_model_name_set.all()

Django modal popup on a list link

I would like to develop a popup on a list link (please see the screenshot) click. popup will be a list also.
So far this is my code below
Model class:
class PSCInscpection(models.Model):
vessel = models.ForeignKey(Vessel, on_delete=models.CASCADE, null=True)
inspection_id = models.CharField(max_length=100, null=False, unique=True)
followup_inspection = models.BooleanField(null=False, default=False)
date = models.DateTimeField(null=True, blank=True)
port = models.CharField(max_length=50, null=True, blank=True)
country = models.CharField(max_length=50, null=True, blank=True)
number_of_defects = models.PositiveIntegerField(null=True, blank=True)
days_detained = models.PositiveIntegerField(null=True, blank=True)
class PSCInscpectionDefects(models.Model):
pscinscpection = models.ForeignKey(PSCInscpection, on_delete=models.CASCADE, null=True)
inspection_id = models.CharField(max_length=100, null=False, blank=False)
defect_code = models.CharField(max_length=50, null=True, blank=True)
defect_text = models.CharField(max_length=255, null=True, blank=True)
class_is_responsible = models.CharField(max_length=50, null=True, blank=True)
defective_item_code = models.CharField(max_length=50, null=True, blank=True)
views.py
inspections = PSCInscpection.objects.filter(
vessel_id=self.kwargs['pk']).order_by('-date')
renderableInspections = []
for _insp in inspections:
item = {
"date": _insp.date.strftime("%Y-%m-%d"),
"port": _insp.port,
"country": _insp.country,
"defects": _insp.number_of_defects,
"detained": _insp.days_detained
}
renderableInspections.append(item)
context['inspections'] = renderableInspections
My list binding from this "PSCInscpection" object and popup will bind from "PSCInscpectionDefects" object. Any help regarding please ? Thanks !
view.html:
{% for inspection in inspections %}
<tr>
<td>{{ inspection.date }}</td>
<td>{{ inspection.country }}</td>
<td>{{ inspection.port }}</td>
<td>
{% if inspection.defects %}
{{ inspection.defects }}
{% else %}
{{ 0 }}
{% endif %}
</td>
<td>{{ inspection.detained|default:0 }} Days</td>
</tr>
{% endfor %}
You should be able to access the defects tied to each inspection through the reverse foreign key relationship, but currently you can't do that because the context you're sending to the template is not the queryset of objects itself. Instead, you constructed a data structure manually to handle this which isn't necessary.
You should send the context as your object queryset so you can access your reverse foreign key relationship directly in the template with inspections.pscinscpectiondefects_set.all. This is very verbose, so I recommend setting a related_name attribute to the pscinscpection field in your PSCInscpectionDefects model to defects or similar so you can instead access it with inspections.defects.all.
Simplify your view to return the filtered queryset instead.
# views.py
context['inspections'] = PSCInscpection.objects.filter(
vessel_id=self.kwargs['pk']).order_by('-date')
return context
I won't provide HTML/CSS/JS code for the modal because this question is Django related. But you should be able to launch one when the user clicks the link to the number of defects. Here's the template change assuming a related_name is set.
# view.html
{% for inspection in inspections %}
<tr>
<td>{{ inspection.date }}</td>
<td>{{ inspection.country }}</td>
<td>{{ inspection.port }}</td>
<td>
{% if inspection.defects %}
{{ inspection.defects.count }} <!-- link to modal here -->
{% for defect in inspection.defects.all %}
{{ defect }}
{% endfor %}
{% else %}
{{ 0 }}
{% endif %}
</td>
<td>{{ inspection.detained|default:0 }} Days</td>
</tr>
{% endfor %}

Can anyone help using M2M and the template properly?

I need to show the name of menu and the quantity of it.
But this webpage doesn't show even when the client's address and their name is working out right.
I've got these models(client side) in my Django project:
class Order(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
address = models.CharField(
max_length=100,
)
created_at = models.DateTimeField(auto_now_add=True)
items = models.ManyToManyField(
Menu,
through='OrderItem',
through_fields=('order', 'menu'),
)
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
menu = models.ForeignKey(Menu, on_delete=models.CASCADE)
count = models.PositiveSmallIntegerField()
and the template page like below.
{% for order in order_set %}
<tr>
<td>{{ order.client.name }}</td>
<td>{{ order.address }}</td>
<td>{% for item in order.items_set.all %}{{ item }}{% endfor %}</td>
<td>{{ order.item_set.all.count }}</td>
</tr>
{% endfor %}
</table>
Models(partner side) like below.
class Menu(models.Model):
partner = models.ForeignKey(
Partner,
on_delete=models.CASCADE,
)
image = models.ImageField(
verbose_name="메뉴 이미지"
)
name = models.CharField(
max_length=50,
verbose_name="메뉴 이름"
)
price = models.PositiveIntegerField(
verbose_name="가격"
)
Can anyone help?
Try this:
{% for order in order_set %}
<tr>
<td>{{ order.client.name }}</td>
<td>{{ order.address }}</td>
<td>{% for item in order.items.all %}{{ item }}{% endfor %}</td>
<td>{{ order.items.all.count }}</td>
</tr>
{% endfor %}
i.e., replace order.items_set.all with order.items.all. There is no relationship defined by items_set. The field you've defined on your Order model is items, so that is what you need to access.
The FOO_set approach is used for relationships in reverse on things like foreign keys. In this case you're following a relationship forward.

If-statement in Django Template does not work

I playing around with a simple addressbook application and I'd like to show the company of a contact in a DetailView of a contact.
In my template:
<table class="table table-bordered table-condensed" style="width:50%;">
{% for company in companies %}
{% if company.name == contact.company %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.street }}</td>
<td>{{ company.plz }}</td>
<td>{{ company.city }}</td>
</tr>
{% endif %}
{% endfor %}
</table>
My view:
class ContactView(DetailView):
model = Contact
template_name = 'contact.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
# Companies
context['companies'] = Company.objects.all()
# Return
return context
In my models:
class Company(models.Model):
name = models.CharField(max_length=255,)
and
class Contact(models.Model):
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255,)
company = models.ForeignKey(Company, blank=True, null=True)
What is wrong with the if statement in my template?
Thanks for your help in advance!
You should compare the company itself, not the name.
Change
{% if company.name == contact.company %}
to
{% if company == contact.company %}

Categories

Resources