Django ordering by multiple fields - django

I am having strange issue with django ordering. For some reason I am not able to order my List View by multiple fields.
Here is my class.
class Syslog(models.Model):
receivedat = models.DateTimeField(default=timezone.now)
facility = models.PositiveSmallIntegerField()
priority = models.PositiveSmallIntegerField()
fromHost = models.CharField(max_length=50)
message = models.TextField(max_length=500)
class Meta:
ordering = ['id','receivedat', 'fromHost']
Here is my view class as well:
class HomePageView(ListView):
model = Syslog
template_name = 'home.html'
context_object_name = 'all_logs'
paginate_by = 10
ordering = ['-id', 'receivedat', 'fromHost']
When a single field in ordering is specified (e.g. ordering = ['-id']), but when I add a second or third parameter everything goes back to defaults (only the first field of ordering is getting recognized)
Does anyone have encounter a similar problem?
The same problem persists in the Django shell as well. When I execute the following query Syslog.objects.all().order_by('id') everything looks good, but when I add another field to order_by (e.g. Syslog.objects.all().order_by('id','fromHost')) the second field doesn't get recognized at all.
Regards,
Jordan

Intsead of this -> Syslog.objects.all().order_by('id','fromHost')
Use this -> Syslog.objects.order_by('id','fromHost')
remove .all()

After slept on my problem I have figured out why I was not getting the result I expected. The issue is that my development data set is quite similar and while the ordering changes it didn't reflect on the template because of objects similarity.

Related

How can i use nested serializer inside the same serializer?

class UserViewSerializer(DynamicFieldsMixin,serializers.ModelSerializer):
followers_set = UserViewSerializer(required=False,many=True)
class Meta:
model = User
fields = ('id','email','username','password','followers_set')
depth = 2
is there anyway i can use this function without getting this error?
followers_set = UserViewSerializer(source='follows',required=False,many=True)
NameError: name 'UserViewSerializer' is not defined
i tried SerializerMethodField but then i can't use depth option there
following_set = serializers.SerializerMethodField()
def get_following_set(self, user):
return UserViewSerializer(User.objects.filter(follows__author=user), many=True).data
using SerializerMethodField gives me error of:
RecursionError at /api/users/
maximum recursion depth exceeded
Can somebody help me please?
The simplest way to handle this is to make three serializers: one without a followers_set, one with a followers_set that uses the previous one, and one that users the second model, so:
# no followers_set
class UserViewSerializer0(DynamicFieldsMixin,serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','email','username')
# followers_set, but only depth 1
class UserViewSerializer1(DynamicFieldsMixin,serializers.ModelSerializer):
followers_set = UserViewSerializer0(source='follows',required=False,many=True)
class Meta:
model = User
fields = ('id','email','username')
# followers_set, with depth 2
class UserViewSerializer(DynamicFieldsMixin,serializers.ModelSerializer):
followers_set = UserViewSerializer1(source='follows',required=False,many=True)
class Meta:
model = User
fields = ('id','email','username')
This is more safe as well, since you can not define recursive loops, unless you indeed work with SerializerMethodField, which is not a good idea if you add extra serializers.
It might however be better no to go to depth two, but stick to depth one. It will generate large responses already, make creating objects more cumbersome, and it will result in a lot of extra queries.

DRF Reverse Relationship Unable to Filter in Serializer

Having an interesting problem with DRF and wondering if anyone has any ideas.
For a simplified example, take these two Django models:
class Vote(models.Model):
user_who_voted = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
vote = models.IntegerField(choices = [(-1, "down"), (1, "up")])
timestamp_voted = models.DateTimeField(auto_now = True)
sample_model = models.ForeignKey('SampleModel', on_delete=models.CASCADE, related_name = 'sample_model_votes')
class Meta:
constraints = [models.UniqueConstraint(fields=['user_who_voted', 'sample_model_id'], name='one_vote_pp_sample_model')]
class SampleModel (models.Model):
#does some stuff
In serializing SampleModel, I want to be able to get the value for vote for the request user (of which there is guaranteed to only be one vote, if any).
In a Django shell, I can pull an instance/item of SampleModel easily:
samplemodelexample = SampleModel.objects.get(pk = 1)
and then I can traverse the reverse relationship to Vote successfully to return the vote value:
samplemodelexample.sample_motel_votes.filter(user_who_voted_id = 1).get().vote
Taking this exact same code (simplified to show relevant portions) into DRF seems to create an issue:
class SampleModelSerializer(serializers.ModelSerializer):
user_vote = serializers.SerializerMethodField()
class Meta:
model = SampleModel
fields = ['user_vote']
read_only_fields = fields
def get_user_vote(self, obj):
try:
vote = obj.sample_model_votes.filter(user_who_voted == self.context['request'].user).get().vote #stacktrace on this line
return f"{vote}"
except:
traceback.print_exc()
return '0'
I get an error on the line indicated that NameError: name 'user_who_voted' is not defined
Does anyone have any idea why?
As some additional background: in practice, there would of course be many users and many votes, but if I put just one vote in the Vote table, then substituting the line generating the error in SampleModelSerializer with
vote = obj.sample_model_votes.first().vote
returns the correct value for vote.
In summary, this seems to be an issue with .filter() in DRF.
Thanks in advance!
You have two of = in your query. You should have one only:
obj.sample_model_votes.filter(user_who_voted=self.context['request'].user).get().vote
Because you used two, Python is thinking it is comparison and evaluating user_who_voted as a variable. Since you don't have that variable, you have this error.

N duplicated queries nested model

I've got an Area model allowing sub areas (you might think of it as categories with subcategories). I reached this by nesting one field to self as foreign key.
class Area(models.Model):
area = models.CharField(max_length=120)
parent = models.ForeignKey('self', models.CASCADE, blank=True, null=True, related_name='subarea')
def __str__(self):
return self.area
With the django rest framwork I've manages to get the correct output. The problem is that when I analyze the request with django-toolbar multiple duplicated requests are made (N*Area(parent=None)). I've solved similar issues by using prefetch_related or select_related. But never done it with a nested model. Is there any way to solve this? Or is this design of the model bad?
I manage to serialize the correct output with the following view and
class ListArea(generics.ListCreateAPIView):
serializer_class = AreaSerializer
queryset = Area.objects.prefetch_related('parent').filter(parent=None)
and serializers
class SubAreaSerializer(serializers.ModelSerializer):
class Meta:
model = Area
fields = ('area','id')
class AreaSerializer(serializers.ModelSerializer):
subarea=SubAreaSerializer(many=True)
class Meta:
model = Area
fields = ('area','id','subarea')
Or might those extra calls be due to the browsable API?
Solution
I solved this with help of the following thread Django: Does prefetch_related() follow reverse relationship lookup?
Instead of
queryset = Area.objects.prefetch_related('parent').filter(parent=None)
I should use
queryset = Area.objects.prefetch_related('parent').prefetch_related('subarea')

django: how do I query based on GenericForeignKey's fields?

I'm new in using GenericForeignKey, and I couldn't make it to work in a query statement. The tables are roughly like the following:
class Ticket(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
class Issue(models.Model):
scan = models.ForeignKey(Scan)
A scan creates one issue, an issue generates some tickets, and I made Issue as a foreign key to Ticket table. Now I have a Scan object, and I want to query for all the tickets that related to this scan. I tried this first:
tickets = Tickets.objects.filter(issue__scan=scan_obj)
which doesn't work. Then I tried this:
issue = Issue.objects.get(scan=scan_obj)
content_type = ContentType.objects.get_for_model(Issue)
tickets = Tickets.objects.filter(content_type=content_type, issue=issue)
Still doesn't work. I need to know how to do these kind of queries in django? Thanks.
The Ticket.issue field you've defined will help you go from a Ticket instance to the Issue it's attached to, but it won't let you go backwards. You're close with your second example, but you need to use the issue_id field - you can't query on the GenericForeignKey (it just helps you retrieve the object when you have a Ticket instance). Try this:
from django.contrib.contenttypes.models import ContentType
issue = Issue.objects.get(scan=scan_obj)
tickets = Ticket.objects.filter(
issue_id=issue.id,
issue_ct=ContentType.objects.get_for_model(issue).id
)
Filtering across a GenericForeignKey can by creating a second model that shares the db_table with Ticket. First split up Ticket into an abstract model and concrete model.
class TicketBase(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
class Meta:
abstract = True
class Ticket(TicketBase):
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
Then create a model that also subclasses TicketBase. This subclass will have all the same fields except issue which is instead defined as a ForeignKey. Adding a custom Manager allows it to be filtered to just a single ContentType.
Since this subclass does not need to be synced or migrated it can be created dynamically using type().
def subclass_for_content_type(content_type):
class Meta:
db_table = Ticket._meta.db_table
class Manager(models.Manager):
""" constrain queries to a single content type """
def get_query_set(self):
return super(Manager, self).get_query_set().filter(issue_ct=content_type)
attrs = {
'related_to': models.ForeignKey(content_type.model_class()),
'__module__': 'myapp.models',
'Meta': Meta,
'objects': Manager()
}
return type("Ticket_%s" % content_type.name, (TicketBase,), attrs)

Sorting Related objects in Django

I have 2 models Category and Item. An Item has a reference to a Category.
class Category(models.Model):
name = models.CharField(max_length=32)
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
The sequence field is supposed to capture the sequence of the Item within a category.
My question is:
What Meta Options do i need to set on category and/or item such that when i do:
category.item_set.all()
that I get the Items sorted by their sequence number.
PS: I am now aware of a meta option called ordering_with_respect_to .. but it is still unclear how it works, and also i have legacy data in the sequence columns. I am open to data migration, if the right approach requires that.
What you're looking for is:
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
class Meta:
ordering = ['sequence',]
That will ensure that Items are always ordered by sequence.
category.item_set.all().order_by('sequence')
Kinda late, and the previous answers don't solve my specific question, but they led me to an answer, so I'm gonna throw this in:
I need to sort my prefetch_related objects specifically for only one view, so changing the default ordering is no good (maybe a model_manager would do it, idk). But I found this in the docs.
I have the following models:
class Event(models.Model):
foo = models.CharField(max_length=256)
....
class Session(models.Model):
bar = models.CharField(max_length=256)
event = models.ForeignKey(Event)
start = models.DateTimeField()
....
class Meta:
ordering = ['start']
Now in a particular view, I want to see all the Events, but I want their Sessions in reverse order, i.e., ordering = ['-start']
So I'm doing this in the view's get_queryset():
from django.db.models import Prefetch
session_sort = Session.objects.all().order_by('-start')
prefetch = Prefetch('sessions', queryset=session_sort)
events = Event.objects.all().prefetch_related(prefetch)
Hope it helps somebody!
*BTW, this is just a simplified version, there are a bunch of filters and other prefetch_related parameters in my actual use case.