Iteration over queryset in a model

370 views
Skip to first unread message

eyscooby

unread,
Oct 4, 2011, 8:27:54 PM10/4/11
to Django users
new to django/python developement, can't get this one figured out.

I have a model that has a couple DateFields (issued_date &
completion_date), and I'm trying to return a value with the difference
of the two on each entry that is complete, and then if it isn't
completed yet, show the amount of days since it was issued.
I am using the Admin interface and what I have in the model is
this....

models.py
class RequestTicket(models.Model):
. . .
issued_date = DateField()
completed_date = DateField(blank=True, null=True)

def days_old(self):
complete = RequestTicket.object.filter(completion_date__isnull=False)
for ticket in complete:
return ticket.completion_date - ticket.issued_date
return date.today() - self.issued.date
days_old.short_discription = 'Days Old'

what i get returned is if the first entry was completed within 2 days
(issued=9/14, completed=9/16), all entries after that get 2 days, even
if there is no completion date.
If i use 'self.object.filter(completion_date__isnull=False)', I get a
NONE answer on all entries
If I don't filter for just completed entries I get an error trying to
subtract NoneType field with DateField, i guess that might be from the
NULL setting.
Any help, advice would be great, or if i need to post in another area.

Django version 1.2

thanks
Kenney

Daniel Roseman

unread,
Oct 5, 2011, 4:11:59 AM10/5/11
to django...@googlegroups.com
OK, there are a few things wrong with your `days_old` function.

Firstly, it operates on a queryset, not an instance, so it should be a method of the Manager, not the Model.  

Secondly, you can't return multiple times like that. You can only return once from a function. You need to build up a list of values, and return that - or set the attribute on each element of the queryset.
--
DR.

eyscooby

unread,
Oct 10, 2011, 2:14:51 PM10/10/11
to Django users
> DR.- Hide quoted text -
>
> - Show quoted text -

DR.
would creating a manager something like this work with a list??

class RequestTicketManager(models.Manager):
def datecalc(self):
complete_list =
list(self.objects.filter(competion_date__isnull=False))
for day in complete_list:
return day.competion_date - day.issued_date

I then put "objects = RequestTicketManager()" in my model

thanks for you help,

Daniel Roseman

unread,
Oct 10, 2011, 3:00:55 PM10/10/11
to django...@googlegroups.com
You're almost there, but you're still trying to return multiple times. The last few lines should be:

    for day in complete_list:
        day.days_old = day.completion_date - day.issued_date
    return complete_list

Actually, since that calculation is fairly trivial, you could probably do it as an instance method on the model class:

    class RequestTicket(models.Model):
        ...
        def days_old(self):
            return self.completion_date - self.issued_date

The difference between this and the method you first proposed is that this only acts on a single instance at a time - so you'll need to get the queryset of completed items in your view in the normal way, then when you iterate through in the template you can just do {{ ticket.days_old }}.
--
DR.

eyscooby

unread,
Oct 11, 2011, 10:17:18 AM10/11/11
to Django users
slowly getting there thanks to your help.
I am actually trying to accomplish this in the Admin interface, so I
am not sure how to use the template tag {{ ticket.days_old }} in that
situation.

the other part I left off yesterday under my model I then had..
(trying to get code formatting correct but keeps going to the left
margin on me when i post)

def days_old(self):
return self.objects.datecalc()
days_old.short_discription = 'Days Old'

more or less is that a correct way I would pull in a custom manager,
lets say if this one didn't have the iteration to it which seems be to
be my problem part now.

thanks

Daniel Roseman

unread,
Oct 12, 2011, 12:30:23 PM10/12/11
to django...@googlegroups.com
On Tuesday, 11 October 2011 15:17:18 UTC+1, eyscooby wrote:
slowly getting there thanks to your help.
I am actually trying to accomplish this in the Admin interface, so I
am not sure how to use the template tag {{ ticket.days_old }} in that
situation.

the other part I left off yesterday under my model I then had..
(trying to get code formatting correct but keeps going to the left
margin on me when i post)

def days_old(self):
    return self.objects.datecalc()
days_old.short_discription = 'Days Old'

more or less is that a correct way I would pull in a custom manager,
lets say if this one didn't have the iteration to it which seems be to
be my problem part now.

thanks

No, because that's not what managers are for. Managers are interacting with the database, and getting and returning one or more new model instances. If you already have an object, and you want to calculate the date on it, you want a model method that simply returns a value, not call the manager.
--
DR.
 

eyscooby

unread,
Oct 17, 2011, 3:28:47 PM10/17/11
to Django users
Ok, sorry I thought I was starting to understand it a little better,
but now I think I took a step backwards, so if it is ok with you let's
step back and take it a step at a time.

So, my first step is wondering if I really need a manager or not??
I was thinking from your first response to me that you might be
suggesting that I did. Let's start there.
If I understand it correctly the Manager is a way of retrieving
specific information.

thanks

Daniel Roseman

unread,
Oct 18, 2011, 4:47:28 AM10/18/11
to django...@googlegroups.com
On Monday, 17 October 2011 20:28:47 UTC+1, eyscooby wrote:
Ok, sorry I thought I was starting to understand it a little better,
but now I think I took a step backwards, so if it is ok with you let's
step back and take it a step at a time.

So, my first step is wondering if I really need a manager or not??
I was thinking from your first response to me that you might be
suggesting that I did.  Let's start there.
If I understand it correctly the Manager is a way of retrieving
specific information.

thanks

Sorry for confusing you. There are two things going on here.

 A Manager is for making custom queries to the database, to return new objects - either one or a queryset of many. Your original code was using `Model.filter()` and modifying the result, so I suggested that it belonged in a manager.

A model method is useful when you want to do a separate, non-database, operation on a single object. That's what you really want to do here - given an instance of RequestTicket, calculate how old it is. There's no iteration contained in the method - you iterate through your existing queryset elsewhere (say in the template) and call days_old on each instance:

    {% for ticket in completed_tickets %}
       {{ ticket.name }}: {{ ticket.days_old }}
    {% endif %}

Or, in your particular circumstance, you simply give the `days_old` method as one of the elements of the `list_display` tuple, and Django takes care of the iterating, calling `days_old` on each row in the changelist.

So in both of these circumstances, `days_old` simply needs to look like this:

    def days_old(self):
        return self.competion_date - self.issued_date 

- so it returns a single value, for the one particular ticket instance which it has been called on.

Hope that helps.
--
DR.

eyscooby

unread,
Oct 18, 2011, 1:04:46 PM10/18/11
to Django users
Ok, this is helping, believe it or not your are helping, I'm probably
confusing myself mostly.

So the model method explanation was very helpful and you are correct
that works great, as long as all dates have a completion_date. If a
new ticket is entered it will fail due to a "NoneType field with
DateField".
That line in my model is as such "completed_date =
DateField(blank=True, null=True)".

The Manager is doing nothing more than returning data from the
database as a query so that makes sense also.

Step two .. is it because of the NULL=True? can't subtract date field
from null field (which makes sense), is there a way around that?
I think this is why i was trying to calculate on a specific filter.

eyscooby

unread,
Oct 24, 2011, 10:12:57 AM10/24/11
to Django users
So with the items that are still waiting to be complete i wanted to
show how many days the ticket has been open by doing something
like ...

return date.today() - self.issued_date

and then those that are complete ...

return self.competion_date - self.issued_date

Can i create 2 managers for this?? one to return completed, and one to
return not completed??
> > objects - either one or aquerysetof many. Your original code was using
> > `Model.filter()` and modifying the result, so I suggested that it belonged
> > in a manager.
>
> > A model method is useful when you want to do a separate, non-database,
> > operation on a single object. That's what you really want to do here - given
> > an instance of RequestTicket, calculate how old it is. There's noiteration
> > contained in the method - you iterate through your existingqueryset
> > elsewhere (say in the template) and call days_old on each instance:
>
> >     {% for ticket in completed_tickets %}
> >        {{ ticket.name }}: {{ ticket.days_old }}
> >     {% endif %}
>
> > Or, in your particular circumstance, you simply give the `days_old` method
> > as one of the elements of the `list_display` tuple, and Django takes care of
> > the iterating, calling `days_old` on each row in the changelist.
>
> > So in both of these circumstances, `days_old` simply needs to look like
> > this:
>
> >     def days_old(self):
> >         return self.competion_date - self.issued_date
>
> > - so it returns a single value, for the one particular ticket instance which
> > it has been called on.
>
> > Hope that helps.
> > --

eyscooby

unread,
Oct 27, 2011, 3:53:04 PM10/27/11
to Django users
I kinda came across an article about creating Proxy Models, that is
what I tried and it really seemed to do what I needed, pretty cool.
Here is what I coded ...(sorry for the formatting, if it looks goofy)

Edit “models.py” file: (create the new Proxy Model)

class RequestTicketCompleted(RequestTicket):
class Meta:
proxy=True
verbose_name = ‘Request Tickets Completed’
verbose_name_plural = ‘Request Tickets Completed’

def days_old(self):
return self.completion_date – self.issued_date
days_old.short_discription = ‘Days Old’

Edit “admin.py” file:

class RequestTicketCompletedAdmin(RequestTicketAdmin):

def queryset(self, request):
qs = super(RequestTicketAdmin, self).queryset(request)
if request.user.is_superuser:
return qs.filter(completion_date__isnull=False)

def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()

admin.site.register(RequestTicketCompleted,
RequestTicketCompletedAdmin)

now that shows up on my main admin page under the my application
section, the superuser can see it, click on it and it will only
display the completed tickets, with the amount of days it took to
complete, then they can also click on the plain Request Tickets and
see any open tickets by all users/techs and how long they have been
open for, sweet.
I had also read where it could be setup as a default manager on the
proxy to provide custom queryset instead of in the admin class, so i
will try and look into do that.

The only thing is that it will not show up on the list of Permissions
to be able to assign to users who would like to see closed tickets as
well, but that is not a really big deal.

Thanks for all your help
> > - Show quoted text -- Hide quoted text -
Reply all
Reply to author
Forward
0 new messages