iexact doesn't work in declaring filterable field in django-filter - django

I am using django-filter lib with DRF.
class OrganizationFilter(FilterSet):
class Meta:
model = Organization
fields = {
'city': ['iexact', 'contains'],
'zipcode': ['exact', 'contains']
}
city: CharField
I want to filter city field case-insensitive.
I can make it work by specifying the following.
class OrganizationFilter(FilterSet):
city = filters.CharFilter(lookup_expr='iexact')
...
Unless If I don't specify the lookup_expr it's not working.
I want to know why?

The reason why, is not working:
fields = {
'city': ['iexact', 'contains'],
'zipcode': ['exact', 'contains']
}
Is because you specified 'contains' next to 'iexact'. Contains will perform a case-sensitive lookup which will be in conflict with you're iexact lookup. So you're code should look like:
class OrganizationFilter(FilterSet):
class Meta:
model = Organization
fields = {
'city': ['iexact'],
'zipcode': ['exact', 'contains']
}

Related

DRF ModelSerializer meta extra_kwargs not working for related fields

I am developing an API in DRF and have stumbled upon one issue that hopefully you can help me to solve. Let's assume I have the following model and a serializer:
class SomeModelBase(models.Model):
property = models.ForeignKey(
Property,
on_delete=models.PROTECT,
)
name = models.CharField()
address = models.OneToOneField(null=True, blank=True)
class SomeModel(SomeModelBase):
...more fields
class SomeModelBaseSerializer(serializers.ModelSerializer):
class Meta:
fields = "__all__"
model = SomeModelBase
extra_kwargs = {"address": {"required": True},}
class SomeModelSerializer(SomeModelBaseSerializer):
class Meta:
model = SomeModel
fields = "__all__""
extra_kwargs = {
"name": {"required": True},
"address": {"required": False},
}
As you can see I am trying to add field kwargs by using extra_kwargs. The issue is that it won't work for a related field e.g. property or address field. I know I can just override it by defining a field manually and passing required=True, and that works but it's too long for my use case. Do you have any idea why this happen and how could i possibly modify required in each child class? Thanks.
I have managed to find the answer in the DRF documentation. I didn't mention that my serializers have some manually declared fields and this is the reason as per DRF documentation:
Please keep in mind that, if the field has already been explicitly declared on the serializer class, then the extra_kwargs option will be ignored.
I think you don't need to upload a foreign key object. You'd better add foreign key fields like property_id or address_id instead of using those objects directly.
So you can define that field in the serializer class.
class SomeModelSerializer(serializer.ModelSerializer):
property_id = serializers.IntegerField(write_only=True, required=True)
address_id = serializers.IntegerField(write_only=True, required=True)
class Meta:
model = SomeModel
fields = "__all__"
extra_kwargs = {
"name": {"required": True},
}

Deserialize Nested Object in Django-Rest-Framework

Good day,
I'm trying to do a PUT request that contains a nested-object and I can't get the province to update correctly. There didn't seem to be anything obvious in the django-rest-framework docs to help with this and I've investigated the solutions of a few other similar problems but none have helped (set many=false, change to ModelSerializer, specialized serializers, etc.).
Everything else about the address will update correctly and return a 200 response (no errors in django logs either). Am I wrong to assume that django-rest-framework handles this all for me for free? Do I have to override the update and create methods within the serializer to validate and save the nested-object?
I'm thinking it's because I have the province serializer set to read_only within the address serializer. However, if I remove the read_only modifier on the province serializer, it gives an error about the province already existing:
{
"province": {
"province": [
"valid province with this province already exists."
]
}
}
Which is behaviour I do not expect and don't know how to resolve. I am not trying to add or update the province. I just want to change the province code in the address.province field and I can't use a string "MB" because it expects an object. I effectively want this behaviour:
UPDATE agent_business_address
SET province = 'MB'
WHERE agent_id = 12345;
-- agent_business_address.province has a foreign key constraint on valid_province.province
-- valid_province is populated with all the 2-letter abbreviations for provinces(
I make this PUT request to /api/agent-business-address/
{
"address": "123 Fake St",
"agent_id": 12345,
"city": "Calgary",
"dlc": "2021-10-11 14:03:03",
"operator_id": 4,
"postal_code": "A1B 2C3",
"province": {
"description": "Manitoba",
"province": "MB"
},
"valid_address": "N"
}
That is received by this ViewSet:
class AgentBusinessAddressViewSet(viewsets.ModelViewSet):
queryset = AgentBusinessAddress.objects.all()
serializer_class = AgentBusinessAddressSerializer
Relevant serializers:
class AgentBusinessAddressSerializer(serializers.HyperlinkedModelSerializer):
province = ValidProvinceSerializer(read_only=True) # Removing the read_only causes the error above.
class Meta:
model = AgentBusinessAddress
fields = ('agent_id', 'operator_id', 'dlc', 'address', 'city', 'province', 'postal_code', 'valid_address')
class ValidProvinceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ValidProvince
read_only_fields = ('operator_id', 'dlc')
fields = ('province', 'description')
Relevant models:
class AgentBusinessAddress(models.Model):
agent = models.OneToOneField(Agent, models.DO_NOTHING, primary_key=True)
operator_id = models.SmallIntegerField()
dlc = models.DateTimeField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=80)
province = models.ForeignKey('ValidProvince', models.DO_NOTHING, db_column='province')
postal_code = models.CharField(max_length=7)
valid_address = models.CharField(max_length=1)
class Meta:
managed = False
db_table = 'agent_business_address'
class ValidProvince(models.Model):
province = models.CharField(primary_key=True, max_length=2)
operator_id = models.SmallIntegerField()
dlc = models.DateTimeField()
description = models.CharField(max_length=30, blank=True, null=True)
class Meta:
managed = False
db_table = 'valid_province'
Any help would be appreciated.
Solved it with the help of the specialized serializers post after a few re-reads.
I updated the serializers to this:
# This gets called for non-GET requests.
class AgentBusinessAddressSerializer(serializers.ModelSerializer):
class Meta:
model = AgentBusinessAddress
fields = ('__all__')
# This get called for GET requests.
class AgentBusinessAddressReadSerializer(AgentBusinessAddressSerializer):
province = ValidProvinceSerializer(read_only=True)
class Meta:
model = AgentBusinessAddress
fields = ('__all__')
I updated the viewset to this:
class AgentBusinessAddressViewSet(viewsets.ModelViewSet):
queryset = AgentBusinessAddress.objects.all()
def get_serializer_class(self):
if self.request.method in ['GET']:
return AgentBusinessAddressReadSerializer
return AgentBusinessAddressSerializer
Now I just send the primary key for the province in the PUT request:
{
"address": "123 Fake St",
"agent": 10000003,
"city": "Calgary",
"dlc": "2021-10-11 19:47:38",
"operator_id": 4,
"postal_code": "A1B 2C3",
"province": "NS",
"valid_address": "N"
}
I get a PUT 200 response back and verify in the DB that the province is now 'NS'.
{
"agent": 10000003,
"operator_id": 4,
"dlc": "2021-10-11T19:47:38",
"address": "123 Fake St",
"city": "Calgary",
"postal_code": "A1B 2C3",
"valid_address": "N",
"province": "NS",
}

Trying to create nested view of django model classes using relations

I am trying to follow this documentation to create nested serializer and api view.
https://www.django-rest-framework.org/api-guide/relations/#nested-relationships
However, I am not able to understand what did I miss as my results are not expected.
I have followed this example to my case and checked various other guides regarding same. Tried different views and different serializer formats.
Code for model is this:-
class Round(models.Model):
player_num = models.IntegerField(null=False, default=1)
class Seats(models.Model):
stack = models.IntegerField(null=False, default=0)
round = models.ForeignKey(Round, on_delete=models.CASCADE)
state = models.IntegerField(choices=STATE)
code for Serializers is this:-
class SeatsSerializer(serializers.ModelSerializer):
class Meta:
model = Seats
fields = ('stack','state')
class RoundSerializer(serializers.ModelSerializer):
seats = SeatsSerializer(many = True, read_only=True)
class Meta:
model = Round
fields = ('player_num','seats')
I want output like this:
{
'player_num': 3,
'seats': [
{'stack': 100, 'state': 'participating', 'name': 'p1', 'uuid': 'ftwdqkystzsqwjrzvludgi'},
{'stack': 100, 'state': 'participating', 'name': 'p2', 'uuid': 'bbiuvgalrglojvmgggydyt'},
]
}
However, output I get is:
{
'player_num': 3,
}
Try this:
round = models.ForeignKey(Round, on_delete=models.CASCADE, related_name='seats')

django rest framework model foreign key

I have two models
class Order(models.Model):
fields...
class OrderItem(models.Model):
fiels...
order = models.ForeignKey(Order, related_name='items')
and I have two serializers:
class CreateOrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
resource_name = 'order-item'
fields = ('order', 'count')
.....
class OrderSerializer(serializers.ModelSerializer):
items = CreateOrderItemSerializer(many=True)
class Meta:
model = Order
resource_name = 'order'
fields = ('id','items')
I posted order with items array like this:
{
'ordername': 'foo',
'items': [{
'name': 'foo1',
},{
'name': 'foo2',
},
]
}
But I have error:
"order":["This field is required."]
how can I first create order later create items with this orderid?
my own answer to question:
first add extra_kwargs to item serializer:
class CreateOrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
resource_name = 'order-item'
fields = ('order', 'count')
extra_kwargs = {
'order': {'required': False},
}
after add create method to orderserializer:
def create(self, validated_data):
items_data = validated_data.pop('items')
order = Order.objects.create(**validated_data)
for item in items_data:
item_name = item['name']
item, created = OrderItem.objects.get_or_create(order=order,name=item_name)
return order
There is a good solution here Django Rest Framework writable nested serializers
which this is almost the same problem here.
The difference is here ForeignKey is used instead of ManyToMany which will cause required field error
adding extra_kwargs solved this problem, there is another way which is add null=True to foreignkey field
order = models.ForeignKey(Order, related_name='items', null=True)

django rest framework - Nested serialization not including nested object fields

i'm trying to get nested object fields populated, however the only thing being returned is the primary key of each object (output below):
{
"name": "3037",
"description": "this is our first test product",
"components": [
1,
2,
3,
4
]
}
How do I have the component model's fields populated as well (and not just the PKs)? I would like to have the name and description included.
models.py
class Product(models.Model):
name = models.CharField('Bag name', max_length=64)
description = models.TextField ('Description of bag', max_length=512, blank=True)
urlKey = models.SlugField('URL Key', unique=True, max_length=64)
def __str__(self):
return self.name
class Component(models.Model):
name = models.CharField('Component name', max_length=64)
description = models.TextField('Component of product', max_length=512, blank=True)
fits = models.ForeignKey('Product', related_name='components')
def __str__(self):
return self.fits.name + "-" + self.name
serializers.py
from rest_framework import serializers
from app.models import Product, Component, Finish, Variant
class componentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ('name', 'description', 'fits')
class productSerializer(serializers.ModelSerializer):
#components_that_fit = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
#fields = ('name', 'description', 'components_that_fit' )
The documented approach doesn't seem to be working for me, and gives me the following error (you can see the lines following the standard approach commented out in the serializers.py entry above:
Got AttributeError when attempting to get a value for field 'components_that_fit' on serializer 'productSerializer'.
The serializer field might be named incorrectly and not match any attribute or key on the 'Product' instance.
Original exception text was: 'Product' object has no attribute 'components_that_fit'.
Update based on answer
Thanks to #Carlton's answer below, here's what is working for me:
serializers.py was changed and now looks like this:
class productSerializer(serializers.ModelSerializer):
components = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
By calling the field components_that_fit, you're having the serialiser look for an attribute by that name. (There isn't one, hence your error.)
Two ways to fix it:
Call the field components, but declare it as components = componentSerializer(many=True)
Set source='components' field option when declaring the components_that_fit field.
The reason you get primary keys is that, unless declared explicitly, relations default to PrimaryKeyRelatedField.
I hope that helps.