Django-filter ordering by SerializerMethodField - django

My filter looks like this:
class CustomUserFilter(django_filters.FilterSet):
has_reviews_in_area = HasReviewsInArea(name='area__id')
has_reviews_in_venue = HasReviewsInVenue(name='venue__id')
class Meta:
model = CustomUser
fields = ['has_reviews_in_area', 'has_reviews_in_venue', 'access_level',]
order_by = ['id', '-id', 'average_rating']
The problem is that average_rating is a dynamic field created in my serializer like this:
average_rating = serializers.SerializerMethodField(source='average_rating')
Which is why I get an error that looks like:
FieldError at /app/users/
Cannot resolve keyword u'average_rating' into field.
Is there anyway I can tell django-filter to filter by my serializer field?
I am using djangorestframework 3.2.4 and django 1.8.4. (With ModelViewsets, ModelSerializers and FilterSets)
Also, I cannot make average_rating a property on my model because it is dynamically calculated according to my filters (e.g. average rating of venue in query parameter)

Related

Django Rest Framework - Filter by custom field

I have Serializer which defines a custom column, in the serializers.py which I am trying to filter a query on.
The itemsoutstanding column example below, I am trying to filter it from my view but it returns "is not defined"
class OverView(serializers.ModelSerializer):
itemsoutstanding = serializers.SerializerMethodField()
class Meta:
model = Order
fields = ['id','total_price', 'created_at','itemsoutstanding']
def get_itemsoutstanding(self, obj):
count= Items.objects.filter(order=obj.id).count()
return count
In my view I am trying to filter on the serializer column, but it says it's not defined
queryset = Order.objects.all()
serializer_class = OverView
queryset = Order.objects.filter(shop=shop)
queryset = queryset.filter(itemsoutstanding> 0)
Is there any way to filter based on the serializer columns?
You will need to annotate the queryset in the definition, you may need to check the django docs for annotations and probably adjust this code to your models but the idea is this:
from django.db.models import Count
queryset = Order.objects.annotate(itemsoutstanding=Count("items"))
Then you can use the itemsoutstanting in the filter and in the serializer as a field:
class OverView(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['id','total_price', 'created_at','itemsoutstanding']
The queryset has nothing to do with the serializer, so it does not know of your defined field in the serializer. That custom field is only added to the serialized data and not the queryset. What you need to do is annotate the field in the view when you get the queryset.

How to set nullable field to None using django rest frameworks serializers

I have a simple view with which I want to be able to set a nullable TimeField to None:
class Device(models.Model):
alarm_push = models.TimeField(null=True, blank=True)
class DeviceSettingsSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ('alarm_push',)
class DeviceSettingsView(RetrieveUpdateAPIView):
serializer_class = DeviceSettingsSerializer
lookup_field = 'uuid'
def get_queryset(self):
return Device.objects.all()
But if I try to PATCH data like {'alarm_push': None} I get an error like {"alarm_push":["Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]]."]}
How can I set alarm_push to None?
As your Serializer is a ModelSerializer DRF will use a TimeField() for your alarm_push model attribute. When you checkout the sourcecode of the DRF Timefield https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L1278 you can see that to_internal_value is raising your error when every attempt of parsing the value failes.
So to have your TimeField be empty you should patch {"alarm_push": ""} with an empty string to represent an empty state.

Django Rest Framework – Custom Hyperlink field in serializer

How can I add a custom hyperlink field in a serializer? I would like to have a hyperlink field in my serializer that has query params in it. Since there is no way to pass query params from HyperlinkedRelatedField or HyperlinkedIdentityField as far as I know, I've tried using a SerializerMethodField. However, this only serializes to a string, and is not a clickable URL when I visit the API through my browser. My code looks something like this:
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view'),
urllib.urlencode({'param': 'foo'})
)
return result
Also, I am having trouble understanding the difference between a HyperlinkedRelatedField and a HyperlinkedIdentityField, so a brief explanation would be appreciated.
This should do the trick:
from rest_framework.reverse import reverse
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
The reverse function in rest_framework takes a view name (whatever view you'd like to link to), either an args list (the object id, in this case) or kwargs, and a request object (which can be accessed inside the serializer at self.context['request']). It can additionally take a format parameter and any extra parameters (as a dictionary) that you want to pass to it.
The reverse function then builds a nice, fully-formed URL for you. You can add query params to it by simply adding as many ?{}&{}&{} to your result variable and then filling in the series of query params beneath the 'param=foo' inside your format function with whatever other params you want.
The HyperlinkedIdentityField is used on the object itself that is being serialized. So a HyperlinkedIdentifyField is being used in place of your primary key field on MyModel because you are using a HyperlinkedModelSerializer which creates a HyperlinkedIdentityField for the pk of the object itself being serialized.
The HyperlinkedRelatedField is used to define hyperlinked relationships to RELATED objects. So if there were a MySecondModel with a foreign key relationship to MyModel and you wanted to have a hyperlink on your MyModel serializer to all the related MySecondModel objects you would use a HyperlinkedRelatedField like so (remember to add the new field to your fields attribute in Meta):
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
mysecondmodels = serializers.HyperlinkedRelatedField(
many=True
read_only=True,
view_name='mysecondmodel-detail'
)
class Meta:
model = MyModel
fields = ('url', 'custom_field', 'mysecondmodels')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
If it were a OneToOneField rather than ForeignKey field on MySecondModel then you would set many=False.
Hope this helps!

Django get all, with related models

Problem:
I'm using Django Rest Framework and i want to fetch all models with the relationships included, like this:
TestModel.objects.all()
My model looks like this:
class TestModel(models.Model):
name = models.CharField(max_length=32)
related_model = models.ForeignKey(TestRelation)
Problem is, i only get the Primary Keys for related_model but i need the whole related_model!
I'm using the ListCreateAPIView, with the above queryset (TestModel.objects.all()) and the most basic form of the ModelSerializer.
I tried the PrimaryKeyRelatedField but i get the same result..
Thanks!
Just create serializer for your related model:
class TestRelationSerializer(serializers.ModelSerializer):
class Meta:
meta = TestRelation
and use is as field in TestModelSerializer:
class TestModelSerializer(serializers.ModelSerializer):
related_model = TestRelationSerializer()
You can also do it other way around, by using TestModelSerializer as field in TestRelationSerializer with many set to true:
class TestRelationSerializer(serializers.ModelSerializer):
testmodel_set = TestModelSerializer(many=True)
just remember, you can't do both at once due to infinite recursion it makes.

raw_id_fields and ManyToMany in Django admin

I want to use raw_id_fields on a ManyToMany relationship in the admin, and I want each related object to show up on its own row (as opposed to a comma-separated list in a single field, which is the default behavior). Following examples spotted in the wild, it seems like I should be able to do this:
# models.py
class Profile(models.Model):
...
follows = models.ManyToManyField(User,related_name='followees')
# admin.py
class FollowersInline(admin.TabularInline):
model = Profile
raw_id_fields = ('follows',)
extra = 1
class ProfileAdmin(admin.ModelAdmin):
search_fields = ('user__first_name','user__last_name','user__username',)
inlines = (FollowersInline,)
admin.site.register(Profile,ProfileAdmin)
But that generates the error:
<class 'bucket.models.Profile'> has no ForeignKey to <class 'bucket.models.Profile'>
I'm not clear what I'm doing wrong here. Thanks for suggestions.
Looks like you are setting the wrong model for your InlineAdmin
as the model for followers you are defining is User and not Profile.
Looking at the docs I'd say you should try:
class FollowersInline(admin.TabularInline):
model = Profile.follows.through
and
class ProfileAdmin(admin.ModelAdmin):
....
exclude = ('follows',)
inlines = (FollowersInline,)
In inline for m2m connection you should use through table and for raw_id_fields setting you should use fields from that through table - inside this table fields can be name different as you expect.
You need goes to sqlite3/psql etc. terminal to see through table schema and use propper field for raw_id_fields.
class FollowersInline(admin.TabularInline):
model = Profile.follows.through
# raw_id_fields = ("follows",) <- wrong
raw_id_fields = ("user",) # <- probably right, because your m2m relation with `User` table and django use name of that table to name field in `through` model