Doctrine QueryBuilder summary functions in Symfony entity repositories? - doctrine-orm

Is there a way I can use min() (or equivalent) in Doctrine's QueryBuilder?
The page I'm working on shows a table of invoices for the current (or impersonated) user. It has a row for each of their invoices and has nested collapsible rows of any associated payments (see below image)
My issue is that in my twig template I need to be able to be identify at the Invoice-level if any of it's Payments have not been approved. If any payment is not approved then I need to flag both the payment and the Invoice row it relates to. For example, in the below example, "Invoice #1" is yellow because it has a nested "Payment #2" record that is yellow (not approved):
To get access to this at the Invoice-level I have been trying to do it through my InvoiceRepository but I'm unclear if/how this can be done using QueryBuilder. I somehow need anyToBeApproved (which isn't an attribute of my Invoice entity) to be available, and still have the Payment information also included:
select a.*, min(b.approved) as anyToBeApproved
from Invoice a
left join Payment b
on a.invoice_id=b.id
where a.member_id=$Uid
group by a.id
My controller:
$invoices = $em->getRepository('AppBundle:Invoice')->OutstandingForUser($this->getUser()->getId());
My QueryBuilder:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i')
->leftJoin('i.member_id','m')
->leftJoin('i.payment_ids','p')
->andwhere('m.member_id = :MemberId' )
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}

I don't know why this hasn't been answered yet, but you should be able to use those aggregate functions just fine.
The following aggregate functions are allowed in SELECT and GROUP BY
clauses: AVG, COUNT, MIN, MAX, SUM
See the documentation: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#aggregate-functions
You can use aggregate functions inside of querybuilder ->select() OK.
I'd rewrite your function as something like:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i, min(p.approved) as anyToBeApproved')
->leftJoin('i.member','m', 'WITH', 'm.member_id = :MemberId') // this works better with not actually constraining your left join, otherwise you might as well just use innerJoin
->leftJoin('i.payment','p')
->groupBy('i.id')
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}
Here's an alternative approach, given you seem to want all the invoice rows anyway.
Change your fetch function to:
public function OutstandingForUser($Uid)
{
return $this->createQueryBuilder('i')
->select('i, p, m') // may as well pre-fetch all entities
->join('i.member','m') // member is surely mandatory? so no left join required
->leftJoin('i.payment','p') // payments may not be present, so left join
->where('m.member_id = :MemberId')
->setParameter('MemberId',$Uid)
->getQuery()
->getResult();
}
Then in your invoice entity do something like
public function hasUnapprovedPayments () {
foreach ($this->getPayments() as $payment) {
if (!$payment->approved()) {
return true;
}
}
return false;
}
public function hasPayments () {
return !$this->getPayments()->isEmpty(); // assuming doctrine collection
}
These functions will be pretty fast as you have already hydrated the entities in the function above.
You could also add a similar isFullyPaid method that loops through payments, but I would personally make that a bool or datetime on Invoice that is set when the invoice is completely paid.
Then in twig you can do stuff like:
{% for invoice in invoices %}
{% if invoice.hasUnapprovedPayments %} do something {% endif %}
{% if invoice.hasPayments %} do something {% endif %}
{# loop through all your payments etc #}
{% endfor %}
Which should make it pretty easy to get the layout you want.

Related

How do i show related foreign key in django query set?

I'm new to django, and i want to show field that related to foreign key in another table. this is the table.
i want to career table got the career_tag_name and hex_code from table color.
i've tried the Career.objects.raw()
this is the query in views.py:
careers = Career.objects.raw('''SELECT website_career_tag.career_tag_name,website_color.hex_code, website_career.*
from website_career INNER JOIN website_career_tag on website_career_tag.id = website_career.career_tag_id_id
LEFT JOIN website_color on website_career_tag.color_id_id = website_color.ID''')
it works perfectly, until i want to use filter() by career_tag_name. when i use query set it's more easy than make it raw to filter.
how do i make those raw query to query set?
It's always better to use django's own ORM rather than raw query and it's quite easy as well. Django querysets always stores all related table informations everywhere, you should just reference to them from the object. For example for career list view you can get all career informations and select the related fields to make loops less expensive in templates:
careers = Career.objects.select_related('career_tag_id', 'career_tag_id__color_id')
And then in a template just refer to related objects as:
{% for career in careers %}
{{ career.career_tag_id.career_tag_name }}
{{ career.career_tag_id.color_id.color_name }}
{% endfor %}

how can I summarize checked column with django-tables2

I have a table with some columns. One column is a checkboxcolumn.
So, how can I summarize the "quantity" columns that checkboxcolumn is checked?
I appreciate if someone can help me.
The django-tables2 docs on CheckBoxColumn mention:
You might expect that you could select multiple checkboxes in the rendered table and then do something with that. This functionality is not implemented. If you want something to actually happen, you will need to implement that yourself.
So django-tables2 doesn't have something build in, we have to write something ourselves. That makes this more of a JavaScript/HTML question, but anyway, let's see if we can create a table for this model:
class Country(models.Model):
name = models.CharField(max_length=100)
population = models.PositiveIntegerField()
This is a basic table, adding an empty footer to the table by adding a footer argument to one of the columns. We will use this footer later to put the calculated population sum in.
class CheckboxTable(tables.Table):
select = tables.CheckBoxColumn(empty_values=(), footer='')
population = tables.Column(attrs={'cell': {'class': 'population'}})
class Meta:
model = Country
template_name = 'django_tables2/bootstrap.html'
fields = ('select', 'name', 'population')
Now, the template. I used jQuery, to quickly create a function to calculate the sum. This function is executed every time the change event is fired on one of the checkboxes, and on page load.
{% load django_tables2 %}
{% render_table table %}
<script src="//code.jquery.com/jquery.min.js"></script>
<script>
$(function () {
// calculate the sum of the population column for the selected rows.
function update_sum() {
var sum = 0;
// loop over each checkbox within <tbody>
$('tbody input[type=checkbox]').each(function () {
// if the checkbox is checked
if ($(this).is(':checked')) {
// find the contents of the population column, convert
// it to integer and add it to the total.
sum += parseInt($(this).parents('tr').find('.population').html());
}
});
// add the sum to the footer cell in the population column.
$('tfoot .population').html(sum);
}
// update the sum after each change
$('tbody input[type=checkbox]').on('change', update_sum);
// update the sum initially.
update_sum();
});
</script>
I added code similar to this to the django-tables2 example application.

Django -- One Template used with multiple variables

I'm trying to display a list of tickets in an HTML table. The table has various headings to display various aspects of the tickets. I would like to present this same table in a bunch of different locations across my project.
I've made a single template of /templates/shared/ticket_list.html in which presents the ticket list and then I {% include %} it where I need to display this listing. Simple enough.
However, there are a couple of pages in my project where I have a Bootstrap tabbed div. I want to display this table of tickets in both tabs, but with a different set of tickets. The HTML for this essentially requires that I {% include %} my table template twice on the same HTML page.
For example:
Tab 1: "Created By User" -- a list of tickets that the current user created
Tab 2: "Assigned To User" -- a list of tickets that are assigned to the current user
In the view, I might have something like:
created_by_ticks = Ticket.objects.filter(created_by = self.request.user)
assigned_to_ticks = Ticket.objects.filter(assigned_to = self.request.user)
The problem is, in my ticket table template, how would I present both querysets since the table itself is likely expecting a single variable name, such as:
{% for t in tickets %}
<tr>...
{% endfor %}
But passing in two querysets of tickets, I now have two ticket variables of created_by_ticks and assigned_to_ticks.
Any ideas how I could use that single ticket table template, but use multiple variables, or some other solution?
You can use the with functionality of the include tag:
{% include 'shared/ticket_list.html' with tickets=created_by_ticks %}
{% include 'shared/ticket_list.html' with tickets=assigned_to_ticks %}

Issues with Fetching the data from database

I can fetch the data like this.
value= mymodel.objects.get(anycondition)
OR
value= mymodel.objects.filter(anycondition)
and can send them to my template with context.
But if I want to select all the data from a table(for all users not only one) as this query does
value= mymodel.objects.all()
and send this value to my template and can see there field by field
e.g.
my table has two fields name and phone no and I use the above query( value= mymodel.objects.all()) now if i want to see all names then i can see that and if i want to see phone no. I can see that too.
I have tried this and it doesn't work and I even I do not know it is possible or not.
If it is possible then please let me know how I can do this ?
I hope you understand my question. !!
Thanks in advance
.all() will return a list of objects that represent the rows in your model. .get() only returns one object. Your template is trying to print the result of all() if it was one object.
This is the same as if you had a list and you wanted to loop through it. In your view you would do:
product = Product_attributes.objects.all()
for i in product:
print i.size
print i.color
The equvalent for the template is:
<ul>
{% for i in product %}
<li>{{ i.size }}</li>
<li>{{ i.color }}</li>
{% endfor %}
</ul>
Although this question isn't clear it seems like you are having a bit of problem with Field Lookups. It is fairly easy to learn Here is a link to get you started

Is it possible to replace values in a queryset before sending it to your template?

Wondering if it's possible to change a value returned from a queryset before sending it off to the template.
Say for example you have a bunch of records
Date | Time | Description
10/05/2010 | 13:30 | Testing...
etc...
However, based on the day of the week the time may change. However this is static. For example on a monday the time is ALWAYS 15:00.
Now you could add another table to configure special cases but to me it seems overkill, as this is a rule. How would you replace that value before sending it to the template?
I thought about using the new if tags (if day=1), but this is more of business logic rather then presentation.
Tested this in a custom template tag
def render(self, context):
result = self.model._default_manager.filter(from_date__lte=self.now).filter(to_date__gte=self.now)
if self.day == 4:
result = result.exclude(type__exact=2).order_by('time')
else:
result = result.order_by('type')
result[0].time = '23:23:23'
context[self.varname] = result
return ''
However it still displays the results from the DB, is this some how related to 'lazy' evaluation of templates?
Thanks!
Update Responding to comments below:
It's not stored wrong in the DB, its stored Correctly However there is a small side case where the value needs to change.
So for example I have a From Date & To date, my query checks if todays date is between those. Now with this they could setup a from date - to date for an entire year, and the special cases (like mondays as an example) is taken care off. However if you want to store in the DB you would have to capture several more records to cater for the side case. I.e you would be capturing the same information just to cater for that 1 day when the time changes. (And the time always changes on the same day, and is always the same)
Update with Solution (Based on answer by KillianDS below)
In models.py i defined a custom property:
#property
def get_corrected_time(self):
from datetime import time
day = datetime.now().weekday()
if day == 0 or day == 1:
self.time = time(12,30)
return self.time
and in the template
{{ object.get_corrected_time|time:"P" }}
The reason for returning a datetime.time object rather then a string is, django's default date & time filters will not work on a string.
Okay, without any precise example (a simple model & clear use case would help), I can't really be sure, but I guess this is what you want to do. In your model definition, add something like this:
#property
def get_corrected_time(self):
if today_is_monday:
return '13:30'
return str(self.time)
in your template you can perfectly call this as {{ object.get_corrected_time }}
Now I see in your template tag you want to do this for example only on the first element in a queryset, you can easily do this as follows in the template (you could also do it in the view method):
{% for object in list %}
{% if forloop.first %}
{{ object.get_corrected_time }}
{% else %}
{{ object.time }}
{% endif %}
{% endfor %}