How to update data in django rest framework - python

I have a cart model and I want in API which if the user the add same items two times in cart the cart will automatically increase the quantity of service. In my case, if I add the same item twice it creates another cart instead of updating the previous one. I search for it a lot but I don't get an answer. I tried a lot to do this. If somebody is capable of giving answer then please give an answer , please
Here is my code:-
views.py
class CartViewSet(viewsets.ModelViewSet):
serializer_class = CartSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
user = self.request.user
if user.is_authenticated:
if user is not None:
if user.is_active and user.is_superuser or user.is_Customer:
return Cart.objects.all()
raise PermissionDenied()
raise PermissionDenied()
raise PermissionDenied()
filter_backends = [DjangoFilterBackend]
filterset_fields = ['date_created', 'user']
#action(detail=False)
def count(self, request):
queryset = self.filter_queryset(self.get_queryset())
count = queryset.count()
content = {'count': count}
return Response(content)
serializers.py
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = ['id','url', 'user', 'service', 'defects', 'date_created', 'quantity' , 'price', 'total']
models.py
class Cart(models.Model):
user = models.ForeignKey('accounts.User', related_name="carts", null=True, on_delete=models.SET_NULL)
quantity = models.IntegerField(default=1)
service = models.ForeignKey('accounts.SubCategory',null=True, on_delete=models.SET_NULL)
defects = models.ForeignKey('Defects',null=True, on_delete=models.SET_NULL)
price = models.IntegerField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
total = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.user.username

You have to override create method in you CartSerializer you can able to check and update if already created. I will mention some stuff tying this
Override Create method
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = ['id','url', 'user', 'service', 'defects', 'date_created', 'quantity' , 'price', 'total']
def create(self,validated_data):
""" Create cart if not created """
cart,created = Cart.objects.get_or_create(**validated_data)
if not created:
cart.quantity=cart.quantity+1
# you have to write your own logic i give you just an hint
return cart
Here we override create method it's invoking whenever we do post a request on the cart URL at the time we can change flow of creating an object of the cart
Hope you understand if you have any query concern let me know

I have discovered some best practices, below I will be showing a generalized way how to update API with function-based views which can be used with class bases views.
*** Note
you must have some id or unique field to identify the objects in the payload of JSON
Models.py
class Product(models.Model):
actual_product_id=models.Autofield(primary_key=True,auto_created=True)
price ....
etc
Serializers.py
from .import Product
class Product_Serializers(models.ModelSerializer):
class Meta:
model=Product
fields='__all__'
Raw Json format which will bw received by api
{ "some_product_id":"4163",
"price ": "41"
}
Urls.py
from django.conf.urls import url
url(r'^/api/productview',views,productview),
Views.py
#api_view(['PUT','GET','POST'])
def productview(request):
json_response=JSONParser().parse(request)
if "some_product_id" in json_response:
doctor_requested_id=json_response['some_product_id']
verified_object=Product.objects.filter(
some_product_id=actual_product_id)
verified_object.update(**json_response)
return JsonResponse({'message':"product has been updated
succesfully"},status=status.HTTP_200_OK)
else :
return JsonResponse({'message':"product's id dosen't exist"})
Hope it can solve the issue .

Related

Django Rest Framework how do I get the id I use in the URL

I have this serializer and I use it to get post detail of a post belonging to a user. The owner of the post is not the user that is currently logged in. I want to check if the post is bookmarked by the currently logged in user. The currently logged in user's id is passed in the request but I cannot find it in this context.
Here is the serializer:
class UserPostSerializer(serializers.ModelSerializer):
images = PostImageSerializer(many=True, read_only=True, required=False)
profile = serializers.SerializerMethodField()
bookmarked = serializers.SerializerMethodField()
class Meta:
model = Post
fields = [
"id",
"category",
"body",
"images",
"video",
"profile",
"published",
"bookmarked",
"created_at",
"updated_at",
]
depth=1
def get_profile(self, obj):
profile_obj = Profile.objects.get(id=obj.user.profile.id)
profile = ShortProfileSerializer(profile_obj)
return profile.data
def get_bookmarked(self, obj):
breakpoint()
bookmark = Bookmark.objects.filter(owner=obj.user.id, post=obj.id,marktype='post')
if bookmark:
return True
else:
return False
The problem is obj.user.id is the owner of the post. I need the logged in user whose id is passed in the url. Here is the model for the bookmark:
class Bookmark(models.Model):
marktype = models.CharField(max_length=50)
post = models.OneToOneField(Post, on_delete=models.CASCADE, null=True, blank=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="updated at")
class Meta:
verbose_name = "bookmark"
verbose_name_plural = "bookmarks"
ordering = ["created_at"]
db_table = "bookmarks"
def __str__(self):
return "{}'s bookmark".format(self.owner.username)
and here is the URL:
path("posts/<int:user>/home/", HomeView.as_view(), name="home"),
This self.context['request'].user returns the owner of the post and not the logged in user.
How do I get the id of the currently logged in user or the user whose id I pass in the URL please?
Maybe do you can use filters to the Viewset:
urls.py
path("posts/home/", HomeView.as_view(), name="home")
viewsets.py
from rest_framework import viewsets
from .models import Post
from .serializers import, UserPostSerializer
from .filters import OwnerFilter
class HomeView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = UserPostSerializer
filter_backends = (OwnerFilter,)
filters.py
from rest_framework.filters import BaseFilterBackend
class OwnerFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
owner = request.query_params.get('owner', None)
if not owner:
return queryset.all()
else:
try:
return queryset.filter(bookmarked__owner__id=owner)
except Exception:
return queryset.none()
Running
Then access the URL:
/posts/home/?owner=OWNER_ID_HERE
Solved it and you can get any kwargs from the view that handles the request. In my case adding the following to the get_bookmarked function gives me the id I send in the URL:
loggeduser = self.context.get('view').kwargs.get('user')

Filtering Foreign key choices in Django serializer

I have a Django model that looks like the code below. At the moment, when I am using django rest framework to create a new menu instance, the dish column contains options created by all users on the platform.
How should I go about filtering the dish column so it only has options created by the user?
Should I be doing it in the views or serializer?
Thank you for the response in advance.
class Dish(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField(max_length=280)
description = models.CharField(max_length=280)
image = models.ImageField(upload_to='static/images/post_image',
default='static/images/post_image/default.jpg')
def __str__(self):
return f'{self.title}'
def get_image_url(self, obj):
return obj.image.url
class Menu(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField(max_length=280)
description = models.CharField(max_length=280)
dish = models.ManyToManyField(Dish)
price = models.SmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(10000)], default=None)
def __str__(self):
return f'{self.title}'
This is how I ended up doing it for those who have the same problems in the future.
class UserDishForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
user = self.context['request'].user
return Dish.objects.filter(user=user)
class MenuCreateSerializer(serializers.ModelSerializer):
dish = UserDishForeignKey(many=True)
class Meta:
model = Menu
fields = ['title', 'description', 'dish', 'price', ]
read_only_fields = ['user', ]
def get_user(self, obj):
return str(obj.user.username)
Assuming that you have an user object, you can get all dishes associated to that user like this:
user.dish_set
If you want to find all menu's that are having particular menus by dish's owner. That can be done like
Menu.objects.filter(dish__user=user)
Placement of this depends on what you are trying to achieve. If you want to validate the input, placement should be in serializer
class UserDishForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
user = self.context['request'].user
return Dish.objects.filter(user=user)
class MenuCreateSerializer(serializers.ModelSerializer):
dish = UserDishForeignKey(many=True)
class Meta:
model = Menu
fields = ['title', 'description', 'dish', 'price', ]
read_only_fields = ['user', ]
def get_user(self, obj):
return str(obj.user.username)

Return a list by a POST Request

I'm new to django and python, I want to return all the objects having the foreign key provided by a post request.
this is my model:
class Product(models.Model):
name = models.CharField(max_length=200)
image = models.CharField(max_length=400)
price = models.CharField(max_length=200)
isFavorite = models.BooleanField(default=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
this is my serializer:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name', 'image', 'price', 'isFavorite')
this is my code in views.py:
class ListProductsOfCategory(generics.ListAPIView):
serializer_class = ProductSerializer()
def post(self, request, *args, **kwargs):
# catch the category id of the products.
category_id = request.data.get("category_id", "")
# check if category id not null
if not category_id:
"""
Do action here
"""
# check if category with this id exists
if not Category.objects.filter(id=category_id).exists():
"""
Do action here
"""
selected_category = Category.objects.get(id=category_id)
# get products of this provided category.
products = Product.objects.filter(category=selected_category)
serialized_products = []
# serialize to json all product fetched
for product in products:
serializer = ProductSerializer(data={
"id": product.id,
"name": product.name,
"image": product.image,
"price": product.price,
"isFavorite": product.isFavorite
})
if serializer.is_valid(raise_exception=True):
serialized_products.append(serializer.data)
else:
return
return Response(
data=serialized_products
,
status=status.HTTP_200_OK
)
this code partially worked, its returning the below reponse.
the problem is that the primary key "id" of the product is missing, I want the response to be like that :
P.S. If anyone can enhance the code and make it less complex I'd be grateful.
Thanks in advance
You're using the serializers the wrong way round. You should pass in the instance and it will give you the serialized data; passing in the data and checking is_valid is for submitting data, not sending it. Also, you can pass in the whole queryset with many=True:
serialized_products = ProductSerializer(products, many=True)
so you don't need your for loop.
But actually DRF will even do all of this for you, because you are using a ListAPIView. All you need to do is tell it what queryset you want, which you do in the get_queryset method. So all you need is:
class ListProductsOfCategory(generics.ListAPIView):
serializer_class = ProductSerializer()
def get_queryset(self):
return Product.objects.filter(category__id=self.request.data['category_id'])

Adding a row to an m2m table using DRF

I have the below representation in my models.py
class Agent(models.Model):
name = models.CharField(max_length=200)
user = models.OneToOneField(SampignanUser, on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Project(models.Model):
client = models.ForeignKey(Client, related_name='projects', on_delete=models.CASCADE)
agent = models.ManyToManyField(Agent)
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
I want to create a rest endpoint where in i can have an agent apply for a particular project (i.e - create a row in the Project-agents table). Is there a particular way i can do this? Right now , i've tried the below approach
urls.py
urlpatterns = [
path('projects/<int:project_id>/apply/', views.project_application, name='apply')
]
views.py
#api_view(['GET','POST'])
def project_application(request, project_id):
if request.method == 'GET':
serializer = ProjectApplicationSerializer()
// show an empty form to the user
return Response(serializer.data)
elif request.method == 'POST':
serializer = ProjectApplicationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
My serializers.py
class ProjectApplicationSerializer(serializers.ListSerializer):
agent = AgentSerializer
project = ProjectSerializer
It doesnt seem to work however , i get the below error from Django
`child` is a required argument.
I can advice you to use serializers.ModelSierializer. So it will look like:
class ProjectModelSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = [..., 'agent',....] # you can use exclude also
def to_representation(self, instance):
self.fields['agent'] = AgentSerializer(many=True)
return super().to_representation(instance)
Here, ModelSerializer automatically handle many to many field. Moreover, in showing your results you can return agent as an object in defining it in to_representation(self, instance) method of serializer. So it will not only return id of agents in array, but extra information as defined AgentSerializer. If you want to create many projects you should use many=True keyword in ProjectModelSerializer( ProjectModelSerializer(data=request.data, many=True)), and request body will change like this:
[
{
.... # project data,
agents = [1,2,3,4,5,...], # primary keys of Agents
},
{
.... # project data,
agents = [1,2,3,4,5,...], # primary keys of Agents
},
]

Django REST: Serializer lookup by UUID

I'm creating this simple shopping API in Django REST.
Internally I'm using IDs for foreign key constraints, while guuids are brought to the outside world.
For the checkout procedure, the user provides a list of article IDs he is willing to purchase. The object in the POST data thus looks as follows:
{
assets: [
{
'product': 'd9d5044d-2284-4d15-aa76-2eee3675035b',
'amount': 4
},
....
]
}
I'm using the following ticket/asset models:
# Ticket
class Ticket(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tickets', on_delete=models.CASCADE)
# Assets
class Asset(models.Model):
ticket = models.ForeignKey(Ticket, related_name='assets', on_delete=models.CASCADE)
stock_item = models.ForeignKey(Stock, related_name='stock_item', on_delete=models.SET_NULL, null=True)
amount = models.IntegerField(validators=[MinValueValidator(0)])
And the serializers look as follows:
# Asset serializer
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ('stock_item', 'amount')
# Ticket serializer
class TicketSerializer(WritableNestedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
assets = AssetSerializer(many=True)
class Meta:
model = Ticket
fields = ('uuid', 'owner', 'assets', )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
When posting an object of the type specified above, the following error is presented:
{"assets":[{"stock_item": ["Invalid type. Expected PK, received string"]}]}
Which I can't seem to solve, how do I instruct the serializer to use the uuid as the lookup value? I solved a similar problem on view-level earlier by using the lookup_field member, but that doesn't seem to solve it. Any suggestions?
Enter code here
If I have understood you correctly, a SlugRelatedField should be able to find the correct related object.
class AssetSerializer(serializers.ModelSerializer):
ticket = serializers.SlugRelatedField(
read_only=True,
slug_field='uuid',
queryset=Ticket.objects.all() # Might be redundant with read_only=True
)
class Meta:
model = Asset
fields = ('ticket', 'stock_item', 'amount')
Elaborating on #BjornW's comment:
class UUIDRelatedField(serializers.SlugRelatedField):
slug_field = 'uuid'
def __init__(self, **kwargs):
super().__init__(slug_field=self.slug_field, **kwargs)
def to_representation(self, obj):
return getattr(obj, self.slug_field).hex

Categories

Resources