I am trying to query and annotate some data from my models:
class Feed(models.Model): # Feed of content
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Piece(models.Model): # Piece of content (video or playlist)
removed = models.BooleanField(default=False)
feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Other fields are not used in the following queries so I skipped them here.
In my view I need to get queryset of all feeds of an authenticated user. Annotation should contain quantity of all pieces that are not removed.
Initially, Piece model didn't contain removed field and everything worked great with the queryset like this:
Feed.objects.filter(user=self.request.user).annotate(Count('piece'))
But then I added the field removed to Piece model and needed to count only pieces that were not removed:
Feed.objects.filter(user=self.request.user)
.annotate(Count('piece'), filter=Q(piece__removed=False))
It gave me the following error:
'WhereNode' object has no attribute 'output_field'
It is only a little fraction of what django outputs on the error page, so if it is not enough, please let me know what else I need to include in my question.
I tried to include output_field with options like models.IntegerField() or models.FloatField() (properly imported) here and there but got some errors which I do not provide here because I believe those actions made no sense.
I am using Django 2.0.3
Your syntax mistake is here,
Feed.objects.filter(user=self.request.user)
.annotate(Count('piece', filter=Q(piece__removed=False)))
filter needs to apply at Count not at annotate.
Reference from Django's documentation:
https://docs.djangoproject.com/en/2.1/topics/db/aggregation/#filtering-on-annotations
Related
I have a model in Django that is used to create an item of stock
class Item(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
description = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='item')
amount = models.IntegerField(default=0, blank=False)
place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name='place')
issue_amount = models.IntegerField(default=0, blank=True)
receive_amount = models.IntegerField(default=0, blank=True)
The item amount will be updated everytime an item is issued by adding or subtracting the issue or receive amount in a views function that alters the instance amount and calls save on that instance.
I want to be able to keep a record of every update and make this information available to both my frontend and to an admin model.
I found this tutorial in which the poster creates a separate model with the same field values as Item and then writes SQL commands directly to the database to create a TRIGGER that saves each Item field on every update: https://www.youtube.com/watch?v=d26DUXynf8s
Is there a way I can replicate this same behaviour using Django?
You want an audit log library.
There are a few (I've never been completely satisfied with any of them) but I quite like this one.
As you can see in the docs, you register your model for auditing like so...
from django.db import models
from auditlog.registry import auditlog
class MyModel(models.Model):
pass # Model definition goes here as usual.
auditlog.register(MyModel) # Register the model.
...and then you can access the log for a particular row via the new history property.
log = MyModel.objects.first().history.latest()
You can browse the various other Django audit log options here, and they are all more or less variations on the same theme.
Django already has this exact feature you're looking for with the LogEntry model. All you need to do is to read the data from the Database.
django.contrib.admin.LogEntry
I have two models: Post and User (the standard Django User model - thus not shown below)
class Post(models.Model):
# post_file = CloudinaryField(resource_type='video',blank=True)
post_file = models.CharField(max_length=500,blank=False,default="")
user = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.CharField(max_length=150,blank=False,default="")
approved = models.BooleanField(default=False)
active = models.BooleanField(default=False)
I want to filter Posts where either the post description or the User.user_name contains a search string.
e.g. search_crit ='erch'
Tried many approaches but the solution keeps evading me.
from django.db.models import Q
active_post_views = Post.objects.filter(active=True, approved=True).select_related('user')
matched_active_post_views = active_post_views.filter(
Q(description__contains=search_crit) |
Q(username__contains=search_crit)
)
The above fails, so obviously not correct. It has a problem with the Q(username__contains=search_crit) portion of the filter. I have also tried Q(user_username__contains=search_crit), to no avail.
Any advice would be much appreciated.
The username is in the user field, so you filter with user__username__contains=…. You thus look "through" relations with two consecutive underscores (__):
matched_active_post_views = active_post_views.filter(
Q(description__contains=search_crit) |
Q(user__username__contains=search_crit)
)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I have a django project and I have a Post model witch look like that:
class BasicPost(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
published = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=False)
body = models.TextField(max_length=999)
media = models.ImageField(blank=True)
def get_absolute_url(self):
return reverse('basic_post', args=[str(self.pk)])
def __str__(self):
return self.title
Also, I use the basic User model that comes with the basic django app.
I want to save witch posts each user has read so I can send him posts he haven't read.
My question is what is the best way to do so, If I use Many to Many field, should I put it on the User model and save all the posts he read or should I do it in the other direction, put the Many to Many field in the Post model and save for each post witch user read it?
it's going to be more that 1 million + posts in the Post model and about 50,000 users and I want to do the best filters to return unread posts to the user
If I should use the first option, how do I expand the User model?
thanks!
On your first question (which way to go): I believe that ManyToMany by default creates indices in the DB for both foreign keys. Therefore, wherever you put the relation, in User or in BasicPost, you'll have the direct and reverse relationships working through an index. Django will create for you a pivot table with three columns like: (id, user_id, basic_post_id). Every access to this table will index through user_id or basic_post_id and check that there's a unique couple (user_id, basic_post_id), if any. So it's more within your application that you'll decide whether you filter from a 1 million set or from a 50k posts.
On your second question (how to overload User), it's generally recommended to subclass User from the very beginning. If that's too late and your project is too far advanced for that, you can do this in your models.py:
class BasicPost(models.Model):
# your code
readers = models.ManyToManyField(to='User', related_name="posts_already_read")
# "manually" add method to User class
def _unread_posts(user):
return BasicPost.objects.exclude(readers__in=user)
User.unread_posts = _unread_posts
Haven't run this code though! Hope this helps.
Could you have a separate ReadPost model instead of a potentially large m2m, which you could save when a user reads a post? That way you can just query the ReadPost models to get the data, instead of storing it all in the blog post.
Maybe something like this:
from django.utils import timezone
class UserReadPost(models.Model):
user = models.ForeignKey("auth.User", on_delete=models.CASCADE, related_name="read_posts")
seen_at = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(BasicPost, on_delete=models.CASCADE, related_name="read_by_users")
You could add a unique_together constraint to make sure that only one UserReadPost object is created for each user and post (to make sure you don't count any twice), and use get_or_create() when creating new records.
Then finding the posts a user has read is:
posts = UserReadPost.objects.filter(user=current_user).values_list("post", flat=True)
This could also be extended relatively easily. For example, if your BasicPost objects can be edited, you could add an updated_at field to the post. Then you could compare the seen_at of the UserReadPost field to the updated_at field of the BasicPost to check if they've seen the updated version.
Downside is you'd be creating a lot of rows in the DB for this table.
If you place your posts in chronological order (by created_at, for example), your option could be to extend user model with latest_read_post_id field.
This case:
class BasicPost(models.Model):
# your code
def is_read_by(self, user):
return self.id < user.latest_read_post_id
I need help in a Django annotation.
I have a Django data model called Photo, and another called PhotoStream (one PhotoStream can have many Photos - detailed models at the end). I get the most recent 200 photos simply by: Photo.objects.order_by('-id')[:200]
To every object in the above queryset, I want to annotate the count of all related photos. A related photo is one which is (i) from the same PhotoStream, (ii) whose timestamp is less than or equal to the time stamp of the object in question.
In other words:
for obj in context["object_list"]:
count = Photo.objects.filter(which_stream=obj.which_stream).order_by('-upload_time').exclude(upload_time__gt=obj.upload_time).count()
I'm new to this, and can't seem to translate the for loop above into a queryset annotation. Any help?
Here's the photo and photostream data models with relevant fields:
class Photo(models.Model):
owner = models.ForeignKey(User)
which_stream = models.ForeignKey(PhotoStream)
image_file = models.ImageField(upload_to=upload_photo_to_location, storage=OverwriteStorage())
upload_time = models.DateTimeField(auto_now_add=True, db_index=True)
class PhotoStream(models.Model):
stream_cover = models.ForeignKey(Photo)
children_count = models.IntegerField(default=1)
creation_time = models.DateTimeField(auto_now_add=True)
So far my attempt has been:
Photo.objects.order_by('-id').annotate(num_related_photos=Count('which_stream__photo__upload_time__lte=F('upload_time')))[:200]
Gives an invalid syntax error.
Whereas the following works, but doesn't cater to my timestamp related requirement in (ii) above:
Photo.objects.order_by('-id').annotate(num_related_photos=Count('which_stream__photo'))[:200]
I broke the query down as follows:
Photo.objects.filter(which_stream__photo__upload_time__lte=F('upload_time')).annotate(num_related_photos=Count('which_stream__photo')).order_by('-id')[:200]
It looks counter-intuitive, but works because of the way this creates the underlying SQL. Good explanation here: https://stackoverflow.com/a/7001419/4936905
So I am trying to setup an entry posting system, where the user can select a bunch of related entries when creating an entry. And it would be wonderful if I could use the InlineModelAdmin for it. But it keeps wanting a foreignkey, which for some reason I'm unable to set up properly.
Here's a simplified setup of my situation:
models.py
class Entry(models.Model):
entry = models.ForeignKey('self', related_name='related_entry', null=True, blank=True)
title = models.CharField(max_length=100, verbose_name='title')
description = models.TextField(verbose_name='description')
def __unicode__(self):
return self.title
admin.py
class EntryInline(admin.TabularInline):
model = Entry
verbose_name = "related entry"
class EntryAdmin(admin.ModelAdmin):
inlines = [
EntryInline,
]
admin.site.register(Entry, EntryAdmin)
The problems im getting are of the likes:
DatabaseError at /admin/app/entry/add/
column app_entry.entry_id does not
exist LINE 1: SELECT "app_entry"."id",
"app_entry"."entry_id", "...
I'm still just kneedeep into the magic world of django, so if someone could point me out where I am going wrong that would be greatly appreciated!
First, I tried the code you provided in my machine (Django 1.2.3, Python 2.6.2, Ubuntu Jaunty) and it worked well as far as I could tell.
where the user can select a bunch of related entries when creating an entry.
Shouldn't you be using a ManyToMany relationship if you want an entry to be related to a bunch of entries? Your code currently defines a ForeignKey instead.
admin.py
...
admin.site.register(Entry, EntryAdmin)
Your admin is presently set up to let the user add an entry and also (optionally) one or more related entries in the same page (this worked perfectly). Was this your expectation?