Django: Order query set by comment count - django

Very simple... I am using Django 1.4.1, and need to order a queryset by the inverse of the number of comments on it. I am using the Django comment framework, and have attempted to use the .annotate(comment_count = Count('comment') structure recommended in other answers... I get 'comment' doesn't resolve to a field error.
I've also tried the 0.3.1 version of django-generic-aggregate, which throws a database error, so that's out.
Photo.objects.filter(galleries=gallery).annotate(comment_count=Count('comments')).order_by('-comment_count')[(page-1)*RESULTS_PER_PAGE:page*RESULTS_PER_PAGE]
Any suggestions?

Put the function under Photo model
class Photo(models.Model):
.........
def orders(self):
//filter first the highest count of comments
aggregate = Photo.objects.aggregate(comment_count=Count('comments'))
return aggregate['comment_count'] + 1
Then you can call it like this in view:
comments = Photo.objects.filter(galleries=gallery).orders()

Related

Django rest framework: automatically create a url for each field of a model

I have large table of data (~30 Mb) that I converted into into a model in Django. Now I want to have access to that data through a REST API.
I've successfully installed the Django REST framework, but I'm looking for a way to automatically create a URL for each field in my model. My model has about 100 fields, and each field has about 100,000 entries.
If my model is named Sample,
models.py
class Sample(models.Model):
index = models.IntegerField(primary_key=True)
year = models.IntegerField(blank=True, null=True)
name = models.TextField(blank=True, null=True)
...97 more fields...
then I can access the whole model using Django REST framework like this:
urls.py
class SampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Sample
fields = ( **100 fields**)
class SampleViewSet(viewsets.ModelViewSet):
queryset = Sample.objects.all()
serializer_class = SampleSerializer
router = routers.DefaultRouter()
router.register(r'sample', SampleViewSet)
But of course my browser can't load all of that data in a reasonable amount of time. I could manually make a different class and URL for each field, but there must be a better way... I want to be able to go to my_site.com/sample/year (for example) and have it list all of the years in JSON format, or my_site.com/sample/name and list all the names, etc.
Please help me figure out how to do this, thanks!
You might be able to do that using a custom viewset route.
You have this:
class ModelViewSet(ModelViewSet):
#list_route()
def sample_field(self, request):
desired_field = request.data.get('field', None)
if not desired_field:
return response # pseudocode
values = Model.objects.all().values_list(desired_field, flat=True)
# serialize this for returning the response
return Response(json.dumps(values)) # this is an example, you might want to do something mode involved
You will be able to get this from the url:
/api/model/sample_field/?field=foo
This extra method on the viewset will create a new endpoint under the samples endpoint. Since it's a list_route, you can reach it using /sample_field.
So following your code, it would be:
mysite.com/sample/sample_field/?field='year'
for example.
There are many interesting details in your question, but with this sample I think you might able to achieve what you want.
Try to use pagination. You can do it in almost the same way as in you question. Pagination in django lets you divide the results into pages. You don't have to display all the entries in the same page. I think this is the best option for you.
Refer django documentation on pagination:
Pagination in django

In Django how to do filter, followed by get queries and get a field value?

Following is a simplified models.py of one of the apps of my Django program:
#This is the custom queryset manager
class TenantManager(models.Manager):
def for_tenant(self, tenant):
return self.get_queryset().filter(tenant=tenant)
#This is one of the models:
class accountChart(models.Model):
name=models.CharField(max_length =200)
remarks=models.TextField(blank=True)
key=models.CharField(max_length=20)
tenant=models.ForeignKey(Tenant,related_name='accountchart_account_user_te nant')
objects = TenantManager()
#This is another data model, related ith FK to 1st model shown here
class paymentMode(models.Model):
name = models.TextField('Payment Mode Name')
payment_account=models.ForeignKey(accountChart,related_name='paymentMode_accountChart')
default=models.CharField('Default Payment Mode ?', max_length=3,choices=choice, default="No")
tenant=models.ForeignKey(Tenant,related_name='paymentmode_account_user_tenant')
objects = TenantManager()
Now, I'm doing the following queryset based on user inout. However, Django is throwing up errors. Request your kind help, as this thing is killing me for more than 2 days.
#Queryset:
payment_mode=paymentMode.objects.for_tenant(request.user.tenant).get(name__exact=request.POST.get('payment_mode'))
payment_account=payment_mode.account
However, Django is throwing up error with the second line of queryset. Even if I use filter instead of get, its showing error - Queryset doesn't have filter!!
From what I understand, first django is giving me all the payment modes related to this user, then getting the payment mode from the request.POST.get object and then in the second line it's trying to get the
related foreignkey. Can anyone kindly tell where I'm going wrong?
Well, sorry to disturb you all, I just got my answer as there was a typo in Queryset.
And as it should be, once I use "get" the solution is fine. Now, I'm not sure if there's a better way to do it except for caching the sessions data!!
This is an answer I wrote for future reference of others.

Filter objects in django which are added before or after a specific Hour of time

I am using django-1.7 in my app. I have a model which has two DateTimeFields. Like
class Task(models.Model):
start_time = models.DateTimeField(null=True, blank=True)
finish_time = models.DateTimeField(null=True, blank=True)
I want to filter the fields start_time and end_time with hours gte and lte so that i can get the queryset which contains Task objects which have to start at a particular time and have a to end at a particular time. What I tried in
Task.objects.filter(start_time__hour__gte=2)
Task.objects.filter(end_time__hour__lte=2)
But this query gives me error
FieldError: Unsupported lookup 'hour' for DateTimeField or join on the field not permitted.
I have tried queryset.raw also which provides correct values but gives error when using with django-filters package.
def start_time_filter(self, queryset, value):
if value.isdigit():
return queryset.raw("select * from app_task WHERE TIME(`start_time`) >= '{0}';".format(value))
return queryset.none()
Help will be appriciated
I'm not sure about this, but by reading the doc sounds like the hour__gte=2 syntax is only added in django 1.9. Previous versions of django only allow exact lookup like hour=2. You might consider upgrading django and try again.
Regarding the raw query error, I don't know what error did you get, but django doc also has the syntax for that. You might look up on the exact sql statement because each sql statement for a database is different. This might solve your problem is you don't want to upgrade django.
django doc about hour lookup in django 1.7
django doc about hour lookup in django 1.9

ManyToManyField causing Django Model Save Problems

I want to save a Django model instance with a ManyToManyField. When I try to do so with the create() manager, it produces the following error:
Exception Value:'post' is an invalid keyword argument for this function
Here is my model:
class Amenity(models.Model):
post=models.ManyToManyField(Post,blank=True,null=True)
name=models.CharField(max_length=50, choices=AMENITIES)
def __unicode__(self):
return str(self.name)
Here is the relevant part of the view:
if request.POST.get('amenities'):
amens=request.POST['amenities'].split(',')
p=int(post.id)
for a in amens:
Amenity.objects.create(post=p,name=a)
return HttpResponse('success')
I'm trying to save multiple amenities at one time and I'd doing so outside of a modelform because I have design in mind and I didn't want to have to create a custom field in this case.
post.id is returning the correct value here, so that doesn't seem to be the issue.
Thanks!
There are two ways you can solve this:
1) By making another database hit: (which is the safest)
p = Post.objects.get(pk=post.id)
if p:
Amenity.objects.create(post=p, name=a)
else:
...
2) Passing the id to post_id
p = int(post.id)
Amenity.objects.create(post_id=p, name=a)
EDIT:
Ok, got it working on my pc. First of all as it is Many to Many, sounds better to use posts not post as a model field. Well anyway this is how you do it:
post = Post.objects.get(pk=id)
for a in amens:
a = Amenity(name=a)
a.post.add(post) #better if it said a.posts.add(post)
a.save()
You might be able to do it via Amenity.objects.create(posts=[p],name=a) since posts expects a list though I haven't tested it myself - all my ManyToMany use a through since they add additional metadata.
You shouldn't pass post.id to post field, cause post is m2m and django take care of it. Where you set post instance ?

How can I use annotate() to count a subset of related models in Django?

I'm trying to use Django's annotate feature to add the count of a related model to a queryset. However, I don't want a full count of related objects, I only want to count the active ones (i.e., "is_active=True"). I can't figure out how to filter down the count.
The (simplified) relevant models:
class Post(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=80)
body = models.TextField()
class Comment(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
comment_body = models.CharField(max_length=80)
is_active = models.BooleanField(default=True)
In a view, I'm trying to annotate a queryset:
queryset=Post.objects.all().annotate(num_comments=Count('comment', distinct=True))
The above counts all the comments related to a post, whereas I only want to count the "is_active" ones. Google and the Django docs aren't helping me here. Has anyone had and solved this problem?
You just need to filter on is_active before doing the annotation:
Post.objects.filter(comment__is_active=True).annotate(num_comments=Count('comment'))
See the explanation here.
This is how I had to "annotate" the number of active comments on my Post queryset:
Post.objects.extra(select={"num_comments":
"""
SELECT COUNT(myapp_comment.id) FROM myapp_reply
WHERE myapp_comment.is_active='1' AND
myapp_comment.post_id = myapp_post.id
"""
},)
Not pretty, but it works. As I mentioned in a comment above, it wasn't possible to use the built-in aggregation function annotate() for this, since that counted all related comments and I only wanted to count the active related comments.
Daniel's solution didn't work, because it filtered out Posts which had no comments. I don't want to filter out any Posts, just inactive comments.
If anyone has a better solution, I will gladly up-vote and best-answer you!
There is two variants based on what database you are using. If you use MySQL the solution is simple and elegant:
Post.objects.annotate(num_comments=Sum('comment.is_active'))
This works because in database boolean fields are integers and True is 1, False is 0.
But this works ONLY in MySQL and works ONLY for boolean fields. The more general way to do the job which works on all databases and can do more complex checks is using a little SQL inserted a little 'hacky':
Post.objects.annotate(num_comments=Count('comment',
field='CASE WHEN myapp_comment.is_active THEN 1 END'))
I have the same problem in my personal blog, and that was the solution. I write a blog post for that. http://venelin.sytes.net/blog/django/filtrirane-na-agregirash-count-v-django/. It's on bulgarian but my site uses google translation. The translation is not very good but may help to understand why this works.