DRF updating data using AJAX call - django

When I try to send data via the frontend to the serializer I get a HTTP 400 error. If I do it directly via the DRF browsable API it works though:
model:
class Shipment(models.Model):
name = models.CharField("name", max_length = 128)
date = models.DateField()
class Product(models.Model):
serial = models.CharField("serial", max_length = 31, unique = True)
shipment = models.ForeignKey(Shipment, on_delete = models.CASCADE, blank = True, null = True)
serializer:
class ShipmentSerializer(serializers.ModelSerializer):
class Meta:
model = Shipment
fields = ["id", "name",]
class ProductSerializer(serializers.ModelSerializer):
shipment = ShipmentSerializer()
def update(self, instance, request):
product = Product.objects.get(serial = instance)
product.shipment = Shipment.objects.get(id = request["shipment"]["id"])
product.save()
return instance
class Meta:
model = Product
fields = ["serial", "shipment",]
lookup_field = "serial"
read_only_fields = ["serial",]
ViewSet:
class ProductViewSet(ModelViewSet):
serializer_class = ProductSerializer
lookup_field = "serial"
http_method_names = ["get", "patch", "put"]
def get_queryset(self):
return Product.objects.all()
AJAX call:
$.ajax({url: `api/products/${serial}/`,
dataType: "json",
contentType: "application/json",
type: "PUT",
data: {"shipment": shipment[0]},
headers: {"X-CSRFTOKEN": csrf_token },
success: function () {window.location = "?msg=ok";},
error: function () {window.location = "?msg=error";}
});
Browser output:
PUT
http://127.0.0.1:8000/api/products/d39f281f/
Status400
Bad Request
VersionHTTP/1.1
Transferred400 B (111 B size)
Referrer Policysame-origin
Request payload:
shipment=4
Response:
{"shipment":["This field is required."]}
or after some playing:
JSON parse error - Expecting value: line 1 column 1 (char 0)
why is the response field is required when there is a payload.

One way to approach this problem is to define two fields for shipment, one for writing and one for reading:
class ProductSerializer(serializers.ModelSerializer):
shipment_id = serializers.PrimaryKeyRelatedField(queryset=Shipment.objects.all(), write_only=True)
shipment = ShipmentSerializer(read_only=True)
class Meta:
model = Product
fields = ["serial", "shipment", "shipment_id"]
When you are updating, you can specify the id of the shipment using shipment_id:
{
"shipment_id": 10,
}
When you are retrieving or listing, the shipment details will come under shipment:
{
"serial": "",
"shipment": {
... # shipment details
}
}

Related

Need to add one model fields in the another model serializers but throwing error while POST the request

models.py
class Product(models.Model):
product_id = models.AutoField(unique=True, primary_key=True)
product_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "product_master"
def __str__(self):
return self.product_name
class Organisation(models.Model):
"""
Organisation model
"""
org_id = models.AutoField(unique=True, primary_key=True)
org_name = models.CharField(max_length=100)
org_code = models.CharField(max_length=20)
org_mail_id = models.EmailField(max_length=100)
org_phone_number = models.CharField(max_length=20)
org_address = models.JSONField(max_length=500, null=True)
product = models.ManyToManyField(Product, related_name='products')
org_logo = models.ImageField(upload_to='org_logo/')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "organisation_master"
def __str__(self):
return self.org_name
serializers.py
class Product_Serializers(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id', 'product_name',)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product')
depth = 1
"
While i tried to do POST method for the organisation model I have tried giving the input for product as "product: 5" and "product: {"product_id": 5,"product_name": "time"} in the postman form data but it is showing as
{
"status": "error",
"code": 400,
"data": {
"product": [
"This field is required."
]
},
"message": "success"
}
Views.py
class Organisation_Viewset(DestroyWithPayloadMixin,viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, ) #ModelViewSet Provides the list, create, retrieve, update, destroy actions.
queryset=models.Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser]
serializer_class=serializers.Organisation_Serializers
def create(self, request, *args, **kwargs):
data = request.data
new_organisation= models.Organisation.objects.create(org_name=data["org_name"],org_code = ["org_code"], org_mail_id =data["org_mail_id"],org_phone_number= data["org_phone_number"], org_address=data["org_address"],org_logo = data["org_logo"])
new_organisation.save()
for product in data["product"]:
product_id = models.Product.objects.get(product_id=product["product_id"])
new_organisation.products.add(product_id)
serializer = serializers.Organisation_serializers(new_organisation)
return Response(serializer.data)
I need to post like this product: {"product_id": 5,"product_name": "time"}, what fields are available in the product model it should be posted on this product field.
Can you please suggest me a way as i tried many ways as per my knowledge but it dosen't worked.
you are using a tuple for fields, put a comma behind product in youre fields. If you use a list then dont use an end comma
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product',)
depth = 1
Update your serializers to:
class Product_Serializers(serializers.Serializer):
product_id = serializers.IntegerField()
product_name = serializers.CharField(max_length=100)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = (
'org_id',
'org_name',
'org_address',
'org_phone_number',
'org_mail_id',
'org_logo',
'org_code',
'product'
)
depth = 1
Update your views as:
class Organisation_Viewset(ModelViewSet):
# ModelViewSet Provides the list, create, retrieve, update, destroy actions.
renderer_classes = (CustomRenderer,)
queryset = Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser, JSONParser]
serializer_class = Organisation_Serializers
def create(self, request, *args, **kwargs):
serializer = Organisation_Serializers(data=request.data)
serializer.is_valid(raise_exception=True)
product_data = serializer.validated_data.pop('product')
does_not_exist = []
product_instances = []
for product in product_data:
try:
product_instance = Product.objects.get(
product_id=product['product_id'],
product_name=product['product_name']
)
product_instances.append(product_instance)
except Product.DoesNotExist:
does_not_exist.append(product)
if len(does_not_exist) > 0:
return Response({
'error': 'Product does not exist',
'does_not_exist': does_not_exist
}, status=400)
organization = Organisation.objects.create(**serializer.validated_data)
for product in product_instances:
organization.product.add(product)
organization.save()
return Response(Organisation_Serializers(organization).data, status=201)
Now we can send the list of product objects for the create API:
curl --location --request POST 'http://localhost:8000/api/organization/' \
--header 'Content-Type: application/json' \
--data-raw '{
"org_id": "12345",
"org_name": "test organization",
"org_address": "test",
"org_phone_number": "12345",
"org_mail_id": "test#te.st",
"org_code": "12345",
"product": [
{
"product_id": 1,
"product_name": "test p1"
},
{
"product_id": 2,
"product_name": "test p2"
}
]
}'

How to solve PUT method is not allowed in drf?

I've a model:
class ListingPrice(Timestamps):
price = models.ForeignKey("Price", on_delete=models.CASCADE)
location = models.ForeignKey("location", on_delete=models.CASCADE)
class Meta:
unique_together = ["price", "location"]
class Price(Timestamps):
package = models.ForeignKey("products.Package", on_delete=models.CASCADE)
locations = models.ManyToManyField("location", through="ListingPrice")
price = models.DecimalField(max_digits=11, decimal_places=3)
with a serializer:
class LocationSerializer(serializers.ModelSerializer):
name = LocalizedField()
class Meta:
model = location
fields = ['id', 'name']
class PriceSerializer(serializers.ModelSerializer):
locations = LocationSerializer(many=True, read_only=True)
class Meta:
model = Price
fields = ['package', 'locations', 'price']
def create(self, validated_data):
print("validated_data, validated_data)
and viewset:
class PriceViewSet(ModelViewSet):
queryset = Price.objects.all()
serializer_class = PriceSerializer
ordering = ['id']
permissions = {
"GET": ["view_minimum_listing_price", ],
"POST": ["add_minimum_listing_price", ],
'PUT': ['update_minimum_listing_price', ],
'DELETE': ['delete_minimum_listing_price', ],
}
In testing I'mm using the following:
data = {
"price": 15,
}
response = self.client.put(path=self.url, data=data, format='json', args=[1])
I'm trying to update the price in the instance with id 1, but neither put or update is not allowed? How to overcome this and update it?
edit: urls.py
router = SimpleRouter()
router.register('listing_price', PriceViewSet, basename='listing_price')

object has no attribute in Django Rest Framework

I develop a mobile application with Django Rest Framework at behind, and React Native at front side.
I have to models and nested serializers. I need to insert record at same time to them. But I have 'Entity' object has no attribute 'automobile' error. When I check similar examples, I do not understand where I am wrong. There will be an entity inserted first, and after that an automobile will inserted with the connection of this entitiy.
Could you please help me?
class Entity(models.Model):
customer = models.CharField(max_length=100, null=True, blank=True)
seller = models.CharField(max_length=100, null=True, blank=True)
entity_name = models.CharField(max_length=50, blank=True, default='')
class Meta:
verbose_name_plural = u"Entities"
verbose_name = u"Entity"
def __str__(self):
return "%s %s" % (self.id, self.entity_name)
class Automobile(models.Model):
entity = models.ForeignKey(Entity, on_delete=models.CASCADE, blank=True)
entity_address = models.CharField(max_length = 250, blank = True, default = '')
used_km = models.IntegerField(default = 0)
manufactured_year = models.IntegerField(validators=[MinValueValidator(1900), MaxValueValidator(timezone.now().year)], blank = True, null = True)
def __str__(self):
return "%s" % (self.entity_id)
class Meta:
verbose_name_plural = u"OptionedAutomobiles"
verbose_name = u"OptionedAutomobile"
class AutomobileSerializer(serializers.ModelSerializer):
class Meta:
model = Automobile
fields = [ 'entity_address', 'used_km', 'manufactured_year']
class EntitySerializer(serializers.ModelSerializer):
automobile = AutomobileSerializer(many=False)
class Meta:
model = Entity
fields = ['id', 'customer', 'seller', 'entity_name',
'automobile']
def create(self, validated_data):
automobile_data = validated_data.pop('automobile')
entity = Entity.objects.create(**validated_data)
Automobile.objects.create(entity= entity, **automobile_data)
return entity
class EntityViewSet(viewsets.ModelViewSet):
serializer_class = EntitySerializer
queryset = Entity.objects.all()
permission_classes = (AllowAny,)
def perform_create(self, serializer):
serializer.save(customer=self.request.user)
class AutomobileViewSet(viewsets.ModelViewSet):
serializer_class = AutomobileSerializer
queryset = Automobile.objects.all()
permission_classes = (AllowAny,)
fetch(`http://127.0.0.1:8000/api/entities/`, {
method: 'POST',
headers: {
'Authorization': `Token ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({seller:seller,
entity_name:entity_name,
automobile:{used_km:10}})
})
.then((res) => res.json())
.then((data) => {
console.log(data)
})
.then(res =>{
Alert.alert("Yeni Entity eklendi.");
})
.catch(error=>console.log(error));
I could not understand what the problem was, but I solved the problem for now by changing location of create function. Now, my create function is under AutomobileSerializer, not EntitySerializer. Of course I also change something about that, and it solved for now as I said.

Django Rest Framework serilize relations

How to serialize a fields in related models.
I got a models:
class Order(models.Model):
order_id = models.BigIntegerField(verbose_name='Order ID', unique=True)
order_name = models.CharField(verbose_name='Order name', max_length=255)
order_type = models.IntegerField(verbose_name='Campaign type')
class Types(models.Model):
delimiter = models.CharField(verbose_name='Delimiter', max_length=255)
status = models.BooleanField(verbose_name='Status', default=True)
title = models.CharField(verbose_name='Title', max_length=255)
class User(models.Model):
name = models.CharField(verbose_name='User name', max_length=200, unique=True)
class Report(models.Model):
order = models.ForeignKey(Order, to_field='order_id', verbose_name='Order ID')
user = models.ForeignKey(User, verbose_name='User ID')
ad_type = models.ForeignKey(Types, verbose_name='Type')
imp = models.IntegerField(verbose_name='Total imp')
month = models.DateField(verbose_name='Month', default=datetime.datetime.today)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
Serializer:
class ReportSerializer(ModelSerializer):
class Meta:
model = Report
depth = 1
I need to get all field like in 'queryset' in get_queryset()
but I got an error:
Got AttributeError when attempting to get a value for field imp on
serializer ReportSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the tuple
instance. Original exception text was: 'tuple' object has no attribute
'imp'.
But if I return in get_queryset() just Report.objects.filter(month=month).all() I'll get all objects and related object with all field, without aggregate of imp and not grouping.
So the question is how to make serializer return structure that set in queryset?
The get_queryset method requires to return a queryset but you are returning a tuple beacause of values_list. Either drop it to return a queryset or go with a more generic view like APIView.
I found a way how to do it.
As I use .values_list() it return list object instead of queryset object. So for serializer do understand what is inside the list I defined all fields in serializer. And in to_representation() I return dictionary like it should be.
Serializer:
class ReportSerializer(serializers.ModelSerializer):
user = serializers.IntegerField()
user_name = serializers.CharField()
order_id = serializers.IntegerField()
order_name = serializers.CharField()
order_type = serializers.IntegerField()
imp = serializers.IntegerField()
class Meta:
model = Report
fields = [
'user', 'user_name', 'order_id', 'order_name',
'order_type', 'imp'
]
depth = 1
def to_representation(self, instance):
Reports = namedtuple('Reports', [
'user',
'user_name',
'order_id',
'order_name',
'order_type',
'imp',
])
return super(ReportSerializer, self).to_representation(
Reports(*instance)._asdict()
)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
def list(self, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True)
# actualy that's it! part of which is below can be pass and just
# return Response(serializer.data)
result = {
'month': parse_date(self.kwargs['month']).strftime('%Y-%m'),
'reports': []
}
inflcr = {}
for item in serializer.data:
inflcr.setdefault(item['user'], {
'id': item['user'],
'name': item['user_name'],
'campaigns': []
})
orders = {
'id': item['order_id'],
'name': item['order_name'],
'type': item['order_type'],
'impressions': item['imp'],
}
inflcr[item['user']]['campaigns'].append(orders)
result['reports'] = inflcr.values()
return Response(result)

Invalid resource lookup data provided (mismatched type)

So I am trying to have my location filter url as api/v1/labels/?brand_location=Australia to filter brands with Australia only brands to return but keep receiving the error message:
{"error": "Invalid resource lookup data provided (mismatched type)."}
But when use api/v1/labels/?brand_location/=Australia it works but doest filter Australia only locations, it returns the full response not excluding locations.
So my questions are:
How can I remove the trailing slash? And get it to filter Australia only locations
Is this the right way to go about this? When using foreign keys with django tastypie?
My code below:
Models.py
class Brand(models.Model):
brand_location = models.ForeignKey('Location', null=True, blank=True, default="")
class Location(models.Model):
state_or_country = models.CharField(unique=True, max_length=200, blank=True, default="", verbose_name=_('Location'),
api.py
class LocationResource(ModelResource):
class Meta:
excludes = ['modified', 'id', 'created']
queryset = Location.objects.all()
resource_name = 'locations'
class LabelResource(ModelResource):
brand_location = fields.ForeignKey(LocationResource, 'brand_location', full=True)
class Meta:
filtering = {
"brand_location": ALL
}
queryset = Brand.objects.all()
resource_name = 'labels'
Snippet JSON Response
{
"labels": [
{
"brand_location": {
"state_or_country": "Australia"
}
}
],
"meta": {
"limit": 6,
"next": "/unlabel-network/unlabel-network-api/v1/labels/?limit=6&brand_location%2F=Australia&offset=6",
"offset": 0,
"previous": null,
"total_count": 128
}
}
api/v1/labels/?brand_location=Australia looks for Location.id=Australia.
Allow filtering deeper:
filtering = {
"brand_location": ALL_WITH_RELATIONS
}
and look for state_or_country field:
api/v1/labels/?brand_location__state_or_country=Australia
I just needed to add filtering to my LocationResource Then add ALL_WITH_RELATIONS to my filtering dict on my LabelResource
class LocationResource(ModelResource):
class Meta:
excludes = ['modified', 'id', 'created']
queryset = Location.objects.all()
resource_name = 'locations'
filtering = {
"state_or_country": ALL
}
class LabelResource(ModelResource):
brand_location = fields.ForeignKey(LocationResource, 'brand_location', full=True)
class Meta:
filtering = {
"brand_location": ALL,
"brand_location": ALL_WITH_RELATIONS
}
queryset = Brand.objects.all()
resource_name = 'labels'