I am creating an api end point using django-rest-framework for a specific json input. I have two related models like so (let's assume post can only have one category):
class Category(models.Model):
name = models.CharField(max_length=10)
slug = models.SlugField()
class Post(models.Model):
category = models.ForeignKey()
title = models.CharField(max_length=100)
text = models.CharField(max_length=256)
and my serializer are simple model serializers:
class CategorySerializer(ModelSerializer):
id = serializers.IntegerField(required=True)
class Meta:
model = Category
class PostSerializer(ModelSerializer):
id = serializers.IntegerField(required=True)
category = CategorySerializer()
class Meta:
model = Post
and my api view is very simple also:
class PostAPIView(mixins.CreateModelMixin, GenericAPIView):
serializer_class = PostSerializer
permission_classes = (IsAuthenticated,)
now in order to create posts I need to parse a json input like this:
{
"id": 10,
"pk": 10
"title": "Some title",
"text": "Some text",
"category": {
"id": 15,
"pk": 15
"name": "Best category",
"slug": "best-category"
}
}
in here 'pk' parameters are crucial for me, I want data to be created on my db using exact pk provided in json. Now if I make a post request and there are no posts with id:10 and categories with id:15 all is fine and data is written to db new records get inserted, but if there are any when rest-framework returns an error like ['Post id 10 already exists'], I would like matching records to be updated according to input instead. How can I do that?
You just need to add the UpdateMixin, just import it like the CreateModelMixin.
This mixin will implement the update and partial update methods, that will do what you want.
But you can not send a POST, for it you will need a PUT, or PATCH. You you want to do this on POST, I recommend you to implement your own create view method.
Related
In my models.py I have the following classes:
class Project(models.Model):
name = models.CharField(max_length=100)
class ProjectMaterial(models.Model):
project = models.ForeignKey("Project", on_delete=models.CASCADE)
material = models.CharField(max_length=150)
units = models.IntegerField()
My serializers are like this:
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = "__all__"
class ProjectMaterialSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectMaterial
fields = "__all__"
My current views.py looks like this:
class ProjectList(generics.ListCreateAPIView):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
class ProjectDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
class ProjectMaterialList(generics.ListCreateAPIView):
queryset = ProjectMaterial.objects.all()
serializer_class = ProjectMaterialSerializer
How should I create my urlpatterns to make a PUT request to change the units value for a
project with an id=1 for a material with an id=3?
I suppose you want to change the value of a Material Object where id = 3. in this case you really dont want to add the FK to the url_patterns. instead you can send data data related to FK via a PUT request.
urlpatterns = [
path('<id>/edit/', MaterialUpdateView.as_view(), name='material-update'),
]
If you really want to change the FK. send the data via a PUT or PATCH request like this
data = {
id: 3,
project: 1,
material: "some material"
units: 25,
}
If you want to update "ProjectMaterial" record with id=3 and that has FK relationship to "Project" record with id=1. All you need is "ProjectMaterial" id in URL and the data that needs to be updated for the corresponding "Project" record(Since it is in relationship with ProjectMaterial).
urlpatterns = [
path('/material/<id>/', ProjectMaterialDetail.as_View(), name='project_material')
]
If you want to update only the "units" field of "ProjectMaterial", you just inherit UpdateModelMixin into the new view class, "ProjectMaterialDetail". You can inherit "RetrieveModelMixin" into the same class. All you need to do is to make sure you send data in correct format to the ProjectMaterial serializer in "PUT" method of "ProjectMaterialDetail" view.
{
id: 5,
units: 152,
}
You can override Update method in serializer or you can call "partial_update" method in "PUT" method.
I'm building a sample iOS app where users would buy and sell products, and I'm trying to design a page where the upper section will have basic profile details and the lower section will have the products that they're currently selling, something like this:
Sample Frontend Image
So I'm trying to create a Serializer / API endpoint which would give me the Profile, with the products that they are currently selling.
My Product model has a ForeignKey relationship to User:
class Product(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=200)
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="product_seller")
category = models.ForeignKey("Category", on_delete=models.CASCADE)
(...)
And I have a Profile model which has a one-to-one relationship with the default Django user. As this view will be based on Profiles, I think it would make more sense to serialize the User or the Profile model and get the products where they are the "seller".
So the JSON response that I want is something like this:
{
"id": 1,
"username": “some username”,
"profile_image": "http://192.168.1.101:8000/images/profile_pictures/732339C5-E419-4A3D-9022-A314416F5F02.png",
"description": “Some description for this particular profile.”
“products”: [
{ “id”: 1,
“image” = http://192.168.1.101:8000/images/abc.jpg,
},
{ “id”: 2,
“image” = http://192.168.1.101:8000/images/abc.jpg,
},
{ “id”: 3,
“image” = http://192.168.1.101:8000/images/abc.jpg,
}
]
}
What would be the best way to approach this using django-rest-framework?
I've tried using Nested Serializers, but my User or Profile models don't explicitly have a relationship to Product, so they haven't worked so far:
class SellerProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "images"]
class SellerSerializer(serializers.ModelSerializer):
products = SellerProductsSerializer(read_only=True, many=True)
profile_image = serializers.CharField(source="profile.profile_image")
description = serializers.CharField(source="profile.description")
class Meta:
model = User
fields = ["id", "username", "profile_image", "description", "products"]
I've also tried using the SerializerMethodField, which I think would work in this case, but I haven't quite figured out how I would filter the products where the particular user is the seller. The endpoints look like this:
.../application/api/seller/1
If I could access the "1" - which is the user id, I could filter with something like:
class SellerSerializer(serializers.ModelSerializer):
products = serializers.SerializerMethodField()
profile_image = serializers.CharField(source="profile.profile_image")
description = serializers.CharField(source="profile.description")
class Meta:
model = User
fields = ["id", "username", "profile_image", "description", "products"]
def get_products(self):
# get the id from the request / or the url
# filter the Product model.
I'm used to doing filtering on the viewsets, but not on the serializer itself. I think filtering on the viewset is not possible in this case, as I'm working with two different models and the one that I'm trying to filter is not the main model for the serializer.
I feel like there has to be a simple way to do this but I have been stuck for quite some time. Any help would be greatly appreciated.
You can retrieve a user with pk in the url,and return a response with that particular user, and the result of the following query:
Product.objects.filter(seller_id=pk)
And add the results to a list,and return a response that looks like this:
Response({
"Seller":Fetched User,
"Their Products":list of products
})
Problem - I have a REST server using django-rest-framework (django v1.7.7, django-rest-framework v3.1.1). In the notifications, I let a user know if they've received a new friend request, or have earned a new badge. There are other notification types as well, but this simple example can explain my problem.
In my GET response, I want to get the notification with a dynamic related object, which is determined by type. if the type is friendreq then I want the relatedObject to be a User instance, with a UserSerializer. If the type is badge, I want to have the relatedObject be a Badge instance, with a BadgeSerializer.
Note: I already have these other serializers (UserSerializer, BadgeSerializer).
Below is what I am wanting to achieve in a response:
{
"id": 1,
"title": "Some Title",
"type": "friendreq"
"relatedObject": {
// this is the User instance. For badge it would be a Badge instance
"id": 1,
"username": "foo",
"email": "foo#bar.com",
}
}
And here are what I have for models and serializer:
# models.py
class Notification(models.Model):
"""
Notifications are sent to users to let them know about something. The
notifications will be about earning a badge, receiving friend request,
or a special message from the site admins.
"""
TYPE_CHOICES = (
('badge', 'badge'),
('friendreq', 'friend request'),
('system', 'system'),
)
title = models.CharField(max_length=30)
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="user")
related_id = models.PositiveIntegerField(null=True, blank=True)
# serializers.py
class NotificationSerializer(serializers.ModelSerializer):
if self.type == "badge":
related_object = BadgeSerializer(
read_only=True,
queryset=Badge.objects.get(id=self.related_id)
)
elif self.type == "friendreq":
related_object = FriendRequestSerializer(
read_only=True,
queryset=FriendRequest.objects.get(id=self.related_id)
)
class Meta:
model = Notification
This code does not work but hopefully it explains what I'm trying to accomplish and the direction I'm trying to go. Maybe that direction is completely wrong and I should be trying to accomplish this by using some other method.
Another option I tried would be to use a SerializerMethodField and perform this in a method, but that seemed not as clean for this case of trying to return a Serialized object based upon another field.
I believe what you're going to want to use is the .to_representation() approach mentioned here in the DRF documentation: http://www.django-rest-framework.org/api-guide/relations/#generic-relationships
I've a simple serializer like this:
class CategoryCreationSerializer(serializers.ModelSerializer):
class Meta:
model = Category
Category has an id field (Django primary key) and I don't get why the Serializer is not updating it.
Scenario: I've a BlogPost with a given category. I'm updating the BlogPost with a different Category (already created on the database). So the POST request will have all the BlogPost information with the new Category JSON object with the updated id:
{
"id": 1,
"title": "My first blog post",
"category": {
"id": 3,
"name": "Techology"
}
}
The problem is that when I'm doing this:
category_data = data.get('category')
if category_data is not None:
category_serializer = CategoryCreationSerializer(data=category_data)
if category_serializer.is_valid():
blog_post.category = category_serializer.object
inside category title will be updated but the id field will be NONE.
Can you explain me why?
Cheers, Emanuele.
By default, the id field that is automatically generated by Django REST Framework is read-only. Because of this, the id will always be none and it will try to create a new Category if there isn't already one attached.
You can override this by adding you own id field to the serializer that is not read-only.
class CategoryCreationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
class Meta:
model = Category
This is the official answer from the issue I created on django rest-framework git repository: https://github.com/tomchristie/django-rest-framework/issues/2114
Short answer though is that by default ModelSerializer will generate a
read-only field for the id. (In the generic views you'll see that the
id is set explicitly by the view code, based on the id in the URL)
You'll want to explicitly declare the id field on the serializer so
that you can make it read-write.
So I managed to solve my problem leaving the Serializer like the one in my question ( without id = serializers.IntegerField() )
category_data = data.get('category', None)
if category_data is not None:
category_serializer = CategoryCreationSerializer(blog_post.category, data=category_data)
if category_serializer.is_valid():
category_serializer.object.id = category_data.get("id", None)
blog_post.category = category_serializer.object
I'm wondering, what is a standard way of updating multiple fields which includes related fields of an instance of a model in django? ...
Class User(models.Model):
id = models.CharField()
name = models.CharField()
dob = models.CharField()
Class Appointment(models.Model):
user = models.ForeignKey(User)
app_id = models.CharField()
time = models.DateTimeField()
status = models.CharField()
To update multiple fields in appointment model I could just do
dict ={
"app_id": "123",
"time": "2012-01-01 10:30",
"status": "Open"
}
Appointment.objects.filter(app_id=123).update(dict)
Is there a way to update the related model?
How could I do an update on Appointment model and User Model?
Case:
dict ={
"name": "foo",
"app_id": "123",
"time": "2012-01-01 10:30",
"status": "Open"
}
For clarity's sake - that's not what 'inherited' means. Appointment is related to user, but does not inherit from it.
The update method on the queryset can't do this in a single call. The docs say:
the only restriction on the QuerySet that is updated is that it can only update columns in the model’s main table, not on related models.
https://docs.djangoproject.com/en/dev/ref/models/querysets/#update
If you have the app_id and nothing else, you can write a second update call:
User.objects.filter(appointment__app_id=123).update(name='foo')