Django Poll for new records - python

In ajax I am polling a django url to retrieve the latest records. I do not want to display any records I have retrieved previously and I only want to retrieve 1 record for each poll request.
What would be the best way to do this?

class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
expire_date = models.DateField()
class Meta:
get_latest_by = 'pub_date'
>>> from mysite.models import Article
>>> Article.objects.latest()
If I'm not wrong in understanding your question, You may go for get_latest_by attribute ofMetaclass and call the methodlatest()` which may serve your purpose, in order not to retrieve the record twice you may use the obj.pk > your_prev_retired_pk.

Hmm. You could do it two ways that I can think of off the bat - there are surely more.
You can add a field called "already_retrieved" and set it to True for those fields that have already been retrieved, and then only grab Whatever.objects.filter(already_retrieved=False).
Also, if they are in order by a pk, you could just keep track of how far you are in the list of pk's.

Related

Django queryset models through an intermediate model

I have a a model Meeting that is related to Signup which in turn is related Attendee.
Models:
class Meeting(models.Model):
date = models.DateTimeField(auto_now=False)
class Signup(models.Model):
meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE, default=1)
...
class Attendee(models.Model, PrintNameTag):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="attendee")
attendee = models.CharField(blank=False, max_length=200)
...
How do I make a queryset on Meeting.objects that will also return the other fields in Signup and Attendee? I need all data for a report. I'd prefer not to change my models because this seems like this should be doable the way they are. I'd also like to query Meeting because I have some other stuff in the query that Meeting is needed for (removed for simplicity).
Here's what I currently have:
qs = Meeting.objects.filter(id__in=queryset).prefetch_related('signup').annotate(attendee_signup=F('signup__attendee_signup'))
I keep getting "cannot resolve keyword attendee_signup into field"
I also have tried variations of:
qs = Meeting.objects.filter(id__in=queryset).select_related('signup').filter(attendee__signup=signup_id)
I get "name 'signup_id' is not defined"
I'm brand new to python and Django and I don't know if I need to use F and/or annotate, prefech_related or select_related. I've looked up what each does, but I can't find the right syntax to get this working. I feel like I've tried every combo and I'm just mashing keys at this point. Could someone help please?
If you add related_name='signups' to the Signup.meeting field, then this should work:
meetings = Meeting.objects.filter(id__in=queryset).prefetch_related('signups__attendee')
The above will fetch all meetings, and all signups for those meetings, and all attendees for those signups. From there you can iterate over everything and it will all have already been fetched from the database:
for m in meetings:
for s in m.signups:
for a in s.attendee:
# whatever
(Incidentally, I would recommend changing that related_name from "attendee" to "attendees" as the plural form tends to be more intuitive.)

queryset value for SlugRelatedField when unique_together applies in django-rest

I'm building a simple API for an ESP8266 to connect to in an IoT application, passing a JSON string. In this application there are multiple Monitors (internet connected devices) per Site (location/address), and multiple LogEntries per Site/Monitor.
The API was originally setup with an endpoint like:
/api/logentries/
Posting a JSON string like:
{"site":"abcd","monitor":"xyz","data_point":"value"}
In the object model, Monitor is a child of Site, but for convenience of entry creation and reporting, the JSON format of the LogEntry posted by each device flattens this structure out, meaning that the LogEntry model also has a FK relationship for both Site and Monitor. In the code below, "textID" is the ID used within the context of the API for the Site/Monitor (e.g. PK values remain "hidden" for API callers).
In models.py:
class Site(models.Model):
name = models.CharField(max_length=32)
textID = models.CharField(max_length=32, blank=True, db_index=True, unique=True)
class Monitor(models.Model):
textID = models.CharField(max_length=32)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
class Meta:
unique_together = ('site', 'textID')
class LogEntry(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
monitor = models.ForeignKey(Monitor, on_delete=models.CASCADE)
data_point = models.CharField(max_length=8, default='')
To get this to work on a single site, I created a custom serializer:
class LogEntrySerializer(serializers.HyperlinkedModelSerializer):
site = serializers.SlugRelatedField(slug_field='textID', queryset=Site.objects.all())
monitor = serializers.SlugRelatedField(slug_field='textID', queryset=Monitor.objects.filter())
class Meta:
model = LogEntry
fields = ('pk', 'site', 'monitor', 'data_point', )
This works for reading valid data, and saving when all monitor IDs are unique across sites.
However, if two sites have a Monitor with the same textID—e.g. "Site1/001" and "Site2/001" this breaks, as the Monitor.objects.all() results in multiple records being retrieved (which makes sense and is expected behaviour).
What I'm wanting to do is to have the second queryset (for monitor) limited to the specified site, to avoid this error.
This post almost answers my question, however it benefits from the second field value (user) being available in the request object, something that is not available in this case.
Is there a way I can retrieve the Site.pk or Site.textID for the queryset value to resolve correctly--e.g. queryset=Monitor.objects.filter(site__textID=xxx)--what would 'xxx' be? Or do I need to completely override the serializer (and not rely on SlugRelatedField)? Or some other approach that might work?
(As an aside: I recognise that this could be achieved by modifying the URL pattern to something like /api///logentries, which would then have this information available as part of the request/context and from a normalisation perspective would be better also. However this would require reflashing of a number of already deployed devices to reflect the changed API details, so I'd like to avoid such a change if possible, even though upon reflection this is probably a cleaner solution/approach long-term.)
Thanks in advance.
You'll need to write your own SlugRelatedField subclass. The unicity constraint that applies to a SlugRelatedField doesn't apply to your case.
This can be done by creating a subfield and overriding the get_value to retrieve the site/monitor tuple and to_internal_value to select the appropriate monitor.
Thanks to the pointers from Linovia, the following field class resolves the issue:
class MonitorRelatedField(serializers.Field):
def to_representation(self, obj):
return obj.textID
def get_value(self, data):
site_textID = data['site']
monitor_textID = data['monitor']
return ( site_textID, monitor_textID, )
def to_internal_value(self, data):
return Monitor.objects.get(site__textID=data[0], textID=data[1])

Optimizing Django ORM query from multiple DB look-ups to possibly one DB look-up

In a Django-based social website I maintain, users post photos. Each posted photo is a part of one photostream (i.e. a list of related photos). I calculate the 200 most recent photos via the get_queryset method of a CBV (ListView):
def get_queryset(self):
return Photo.objects.order_by('-id')[:200]
Next, for each photo, I append the count of the number of related photos exist. That I do by first checking which photostream each photo belongs to, then getting other photos from the said stream, and lastly excluding some based on freshness. 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()
The count is then paired with each obj so that I end up with a dictionary to use in populating the template. As you would have guessed, I basically use this info to show the count of related photos alongwith each listed photo.
But doing it this way is just too many DB lookups! How can I optimize this, for performance? Please advise!
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)
Plesae check if you could use Conditional Aggregations like this:
from django.db.models import Count, Case, When, IntegerField
Photo.objects.annotate(
count=Count(Case(
When(which_stream__photo__upload_time__lte=F('upload_time')), then=1),
output_field=IntegerField(),
))
).order_by('-id')[:200]
I haven't tested this but I think you will get an idea how to use it.

Writing correct SQL query in extra() modifier in django queryset

I need help in writing some SQL for an extra() modifier used within a django queryset. Getting stuck in elegantly framing it, several retries have led to dead-ends. Help!
Background: I have a website where people submit interesting URLs (called Link in my models.py). Other users can post comments under each link posted (called Publicreply in my models.py).
Here's what I want to do: for every user, I want to produce a queryset that -
(i) contains only links the user commented under
(ii) is sorted by the timestamp of the most recent comment under every link. E.g. if a link has multiple comments, I want the most recent comment's timestamp to sort the said link in the queryset. Note that this comment can be from anyone (not necessarily from the user herself).
Now for some simple code, straight from my views.py:
class UserActivityView(ListView):
model = Link
slug_field = "username"
template_name = "user_activity.html"
paginate_by = 10
def get_queryset(self):
comments_by_user = Publicreply.objects.filter(submitter=user)
links_with_comments = [reply.answer_to.id for reply in replies_by_user.iterator()]
qset = Link.objects.filter(id__in=links_with_comments).extra(select={'submitted_on':"SELECT max('submitted_on') FROM 'links_publicreply' WHERE 'links_publicreply.answer_to' = 'links_link.id'"})
return qset
And models.py contains:
class Publicreply(models.Model):
submitter = models.ForeignKey(User)
answer_to = models.ForeignKey(Link)
submitted_on = models.DateTimeField(auto_now_add=True)
description = models.TextField(validators=[MaxLengthValidator(250)])
class Link(models.Model):
submitter = models.ForeignKey(User)
submitted_on = models.DateTimeField(auto_now_add=True)
url = models.URLField(max_length=250)
The SQL I wrote in the extra() modifier in my views.py under the get_queryset() method is not giving the desired result (I'm getting None). Note that I'm trying to use the extra() modifier to attach the most recent timestamp of a publicreply instance to its related link. I'll later sort the queryset according to the said timestamp.
Please help out in formulating the correct SQL in the extra() modifier. I use Django 1.5 and Python2.7. Thanks.

Queryset API distinct() does not work?

class Message(models.Model):
subject = models.CharField(max_length=100)
pub_date = models.DateTimeField(default=datetime.now())
class Topic(models.Model):
title = models.CharField(max_length=100)
message = models.ManyToManyField(Message, verbose_name='Discussion')
I want to get order all the topics according to the latest message object attached to that topic.
I executed this query but this does not give the distinct queryset.
>> Topic.objects.order_by('-message__pub_date').distinct()
You don't need distinct() here, what you need is aggregation. This query will do what you want:
from django.db.models import Max
Topic.objects.annotate(Max('message__pub_date')).order_by('-message__pub_date__max')
Though if this is production code, you'll probably want to follow akaihola's advice and denormalize "last_message_posted" onto the Topic model directly.
Also, there's an error in your default value for Message.pub_date. As you have it now, whenever you first run the server and this code is loaded, datetime.now() will be executed once and that value will be used as the pub_date for all Messages. Use this instead to pass the callable itself so it isn't called until each Message is created:
pub_date = models.DateTimeField(default=datetime.now)
You'll find the explanation in the documentation for .distinct().
I would de-normalize by adding a modified_date field to the Topic model and updating it whenever a Message is saved or deleted.

Categories

Resources