IntegrityError at /api/order/create NOT NULL constraint failed: store_order.product_id - django-rest-framework

I'm trying to create Order via Api request with related keys, product and user id's. But getting an error that they cannot be null, although they're not null.
model.py
class Order(models.Model):
id = models.AutoField(primary_key=True)
product = models.ForeignKey(Product, related_name='product', on_delete=models.CASCADE, default = None)
user = models.ForeignKey(User, related_name='user', on_delete=models.CASCADE, default = None)
orderPrice = models.IntegerField(default=0)
status = models.CharField(max_length=255, default='Принят на обработку')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
serializer.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['id', 'product', 'user', 'status', 'orderPrice']
depth = 1
view.py
#api_view(['POST'])
def OrderCreateView(request):
order_serializer = OrderSerializer(data=request.data)
if order_serializer.is_valid(raise_exception=True):
order_serializer.save()
return JsonResponse(order_serializer.data, safe=False)
and finally my api request
{
"product": 1,
"user": 3,
"status": "Принят на обработку",
"orderPrice": 30000
}
This code worked sometime but i did couple changes and now it getting an error. I tried to remigrate all my models, and requested like that "user_id", "product_id". But keeps getting the error

Hi
the problem of your code is your serializer because you mention a depth of 1 and you send a JSON in your POST functiona JSON in coherent.
Your json should contain all the attributes of your foreign keys like this:
{
"product": {
...attributes of product
},
"user": {
...attributes of user
},
"status": "Принят на обработку",
"orderPrice": 30000
}

You just have to declare the product field as nullable, because null is False by delfault:
class Order(models.Model):
[...]
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)
[...]
I also removed related_name, as to retrieve the order of a product, you had to call product.product. Now you retrieve it using product.order.

Related

are django querysets fastest?

models.py
class Comments(UUID):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
post = models.ForeignKey(Posts, on_delete=models.CASCADE)
comment = models.ForeignKey(
"self", on_delete=models.CASCADE, blank=True, null=True
)
text = models.TextField()
files = models.ImageField()
serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comments
fields = "__all__"
extra_kwargs = {
"user": {"read_only": True},
"text": {"required": False},
"files": {"required": False}
}
def create(self, validated_data):
user = self.context["request"].user
return Comments.objects.create(user=user, **validated_data)
views.py
class PostCommentsRetrieveAPIView(generics.RetrieveAPIView):
serializer_class = CommentSerializer
def get(self, request, *args, **kwargs):
replies_count = Subquery(Comments.objects.filter(
comment=OuterRef("pk")
).values("comment").annotate(count=Count("pk")).values("count"))
comment_remarks = Subquery(Remarks.objects.filter(
comment=OuterRef("pk")
).values("comment").annotate(count=Count("pk")).annotate(
popularities=popularities
).values("popularities"))
# https://stackoverflow.com/questions/63020407/return-multiple-values-in-subquery-in-django-orm
replies = Subquery(Comments.objects.filter(
comment=OuterRef("pk")
).values("comment", "created_at", "updated_at").annotate(
created=Now() - F("created_at"), created_=created_,
updated=Now() - F("updated_at"), updated_=updated_
).annotate(
details=ArrayAgg(
JSONObject(
id="id",
user_id="user_id",
username="user__profile__username",
text="text",
files="files",
created="created_",
updated="updated_",
profile_link=profile_link,
profile_image=profile_picture,
comment_remarks=comment_remarks,
replies_count=replies_count
)
)
).values("details"))
comment = Subquery(Comments.objects.filter(
post=OuterRef("pk"), comment=None
).values("post", "created_at", "updated_at").annotate(
created=Now() - F("created_at"), created_=created_,
updated=Now() - F("updated_at"), updated_=updated_
).values("created_").annotate(
details=ArrayAgg(
JSONObject(
id="id",
user_id="user_id",
username="user__profile__username",
text="text",
files="files",
created="created_",
updated="updated_",
profile_link=profile_link,
profile_image=profile_picture,
comment_remarks=comment_remarks,
comment_replies=replies
)
)
).values("details"))
post = Posts.objects.filter(id=kwargs["post_id"]).annotate(
comment=comment,
).values("comment")
return Response(post, status=status.HTTP_200_OK)
Now I just want to ask is that a best way show comments on post with nested replies, I create another files to calculate datetime, profile link, profile image, remarks on comment
this query is hitting database only one time my question is will it be fastest query to get data from database?
how to can I check that my query is fastest or not

Handle complex request formats and return response

I am building an API using Django rest framework. My API has to receive complex requests that are somewhat different from my models. More specifically the request has to include fields that do not exist in my models.
As an example I have included one of the models and the request that will be received by the API.
from django.db import model
class Author(models.Model):
first_name = models.CharField(max_length=9, blank=False)
last_name = models.CharField(max_length=9, blank=False)
birth_year = mmodels.CharField(max_length=9, blank=False)
and the json request is
{
"general": {
"name": "John",
"surname": "Doe"
},
"details": [
"1980"
]
}
How can I parse that request efficiently, store the data in my database and finally return a response similar to the request?
My approach so far is to create a serializer like the following and modify its create() and to_represent() methods, however this approach seems very dirty especially with nested relationships.
class AuthorSerializer(serializers.ModelSerializer):
general = serializers.DictField(write_only=True)
details = serializers.ListField(write_only=True)
class Meta:
model = Author
fields = [
"id", "general", "details",
]
def create(self, validated_data):
author_data = {}
author_data['first_name'] = validated_data['general']['name']
author_data['last_name'] = validated_data['general']['surname']
author_data['birth_year'] = validated_data['details'][0]
author = Author.objects.create(**author_data)
return author_data
def to_representation(self, instance):
final_representation = OrderedDict()
final_representation["id"] = instance.id
final_representation["general"] = {}
final_representation["general"]["name"] = instance.first_name
final_representation["general"]["surname"] = instance.last_name
final_representation["details"] = [instance.birth_year]
return final_representation

Alter Queryset before returning in DRF

I have a model structure similar to the one below:
Store -> some store fields
Books -> some book fields, FK to Store
BookProperty -> name, value, FK to Books (a one to many relationship), FK to store
The book property can store any info for the book eg. no_of_pages, publisher etc added by the store.
I need to make an API endpoint where I can get all BookProperty for a store.
I used the url:
/stores/:store_id/bookproperty
Used a ModelSerializer for BookProperty with fields = [publisher, no_of_pages]
Used a genericViewSet with a ListModelMixin.
The endpoint turned out like this below:
{
"count": 4,
"next": null,
"previous": null,
"results": [
{
"name": "publisher",
"value": "somePublisher"
},
{
"name": "pages",
"value": "1000"
},
{
"name": "publisher",
"value": "someOtherPublisher"
},
{
"name": "publisher",
"value": "somePublisher"
}
]
}
The problem with this is that multiple objects can have the same name, value pairs. I need this information in a way where all the objects are unique and grouped kind of like this:
{
{"name":"publisher", "value":["somePublisher", "someOtherPublisher"]},
{"name":"pages", "value":["1000"]},
}
I'm trying to override the get_queryset(self) but it's not working.
Any help would be appreciated. Thanks in advance!
EDIT:
models.py
class BookProperty(models.Model):
books = models.ForeignKey(
Books,
on_delete=models.CASCADE,
)
name = models.CharField(max_length=100)
value = models.CharField(max_length=100)
store = models.ForeignKey(
"Store",
on_delete=models.CASCADE,
)
serializers.py
class BookPropertySerializer(serializers.ModelSerializer):
class Meta:
model = models.BookProperty
fields = ["name", "value"]
views.py
class BookPropertyViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = serializers.BookPropertySerializer
I think that instead of overriding the get_queryset(self) I should try changing the def list(self, request, *args, **kwargs) :
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
tag_dictionary = defaultdict()
things = list(queryset)
for key, group in itertools.groupby(things, lambda x: x.name):
for thing in group:
if key not in tag_dictionary.keys():
tag_dictionary[key] = [thing.value]
else:
tag_dictionary[key].append(thing.value)
for key in tag_dictionary.keys():
tag_dictionary[key] = list(set(tag_dictionary[key]))
return Response(json.dumps(tag_dictionary))
The above solution is working but might not be the best one.

Django rest_framework not serializing relationships

I am trying to serialize Foreign keys inline with Django rest_framework. Foreign keys are used to link lookup tables as per a normal DB normalisation setup.
An example of my model with the lookup:
class OrderStatus(models.Model):
StatusId = models.PositiveSmallIntegerField(primary_key=True)
StatusDescription = models.CharField(max_length=50)
class Order(models.Model):
OrderId = models.AutoField(primary_key=True)
OrderDate = models.DateTimeField(auto_now_add=True)
Status = models.ForeignKey(OrderStatus, on_delete=models.CASCADE)
My serializers:
class OrderStatusSerializer(serializers.ModelSerializer):
class Meta:
model = OrderStatus
fields = ['StatusId', 'StatusDescription']
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('OrderId', 'OrderDate', 'Status')
What I obtain when I call the REST API is the following:
{
"type": "Order",
"id": "1",
"attributes": {
"OrderId": 1,
"OrderDate": "2020-05-19T08:23:54"
},
"relationships": {
"Status": {
"data": {
"type": "OrderStatus",
"id": "1"
}
}
}
}
I would like to have the Status inline in the "attributes", either as a simple id or even as a JSON object with the two values inline. Both are good options, as long as it's not in that "relationships" field.
I tried to add the following to OrderSerializer:
Status = serializers.PrimaryKeyRelatedField(queryset=OrderStatus.objects.all())
No difference.
I tried the following:
Status = OrderStatusSerializer(many=False)
No difference.
I tried all the other options in
https://github.com/encode/django-rest-framework/blob/master/docs/api-guide/relations.md
including the SlugField to include the description instead that the ID, with no result.
It seems that what I change has no effect on the serialization.
As per my own comment, the issue was caused by rest_framework_json_api.renderers.JSONRenderer.
By switching it back to rest_framework.renderers.JSONRenderer the expected documented behaviour has been re-established.

How to filter records based on nested data using django rest framework

I have this json response from my customer api based on django rest framework.
When I hit api
http://localhost:8000/api/customers
I receive following response
[
{
'name': 'Daniel',
'group': 'BK',
'user_id': 102,
'user_details': {
'username': 'dan1',
'active': true,
}
},
{
'name': 'John',
'group': 'BK',
'user_id': 103,
'user_details': {
'username': 'john1',
'active': true,
}
}
]
Now I need to filter record whose username=john1, how do I do that?
I have tried using this in my customer viewset by defining filter backend
filter_fields = ('user_details__username',)
and tried hitting the api as
http://localhost:8000/api/customers?user_details__username=john1
but it gives error as
'Meta.fields' contains fields that are not defined on this FilterSet:
user_details__username
Its happening because user_details is not the field of my customer serializer, its basically SerializerMethodField which manipulates user information to display under customer api.
Here is my customer serializer
class CustomerSerializer(serializers.HyperlinkedModelSerializer):
user_details = serializers.SerializerMethodField('get_serialized_target_object')
class Meta:
model = Customer
fields = '__all__'
def get_serialized_target_object(self, obj):
usr_id = obj.user_id
if usr_id:
instance = User.objects.filter(pk=usr_id)
if instance:
instance = instance[0]
return UserSerializer(instance=instance).data
else:
return None
and here is my viewset
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
filter_fields = ('user_details__username',)
Please help me how do I filter my record from customer api with username=john1
You should not use SerializerMethodField.
Try the serializer below:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'active',)
class CustomerSerializer(serializers.ModelSerializer):
user_details = UserSerializer(many=True)
class Meta:
model = Customer
fields = '__all__'
Then you can define your field as filter_fields = ('user_details__username',)

Resources