When I create this model, the values are nulls.
class TestRequest(models.Model):
class Meta:
verbose_name_plural = "TestRequest"
title = models.CharField(max_length=256, null=True, blank=True)
testConfiguration = models.ForeignKey(
TestConfiguration, on_delete=models.PROTECT, null=True, blank=True)
testDescription = models.ForeignKey(
TestDescription, on_delete=models.PROTECT, null=True, blank=True)
The serializer:
class TestRequestSerializer(serializers.ModelSerializer):
class Meta:
model = TestRequest
fields = [
'id',
'title',
'testConfiguration',
'testDescription',
]
depth = 2
The view:
#api_view(['PUT'])
def TestRequestUpdate(request, pk):
testRequest = TestRequest.objects.get(id=pk)
serializer = TestRequestSerializer(instance=testRequest, 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)
And when I want to update them later from the front-end with this state:
id: 98
title: "title"
testConfiguration: 31
testDescription: 32
I get this response:
{
"id": 98,
"title": "title",
"testConfiguration": null,
"testDescription": null
}
Why can't I update it?
EDIT: I added my solution as an answer.
You can modify your views with the following code:
testRequest = TestRequest.objects.get(id=pk)
import json
data = json.loads(json.dumps(request.data))
if data.get('testConfiguration', None) is None and testRequest.testConfiguration:
data.update({'testConfiguration': testRequest.testConfiguration.id})
if data.get('testDescription', None) is None and testRequest.testDescription:
data.update({'testDescription': testRequest.testDescription.id})
serializer = TestRequestSerializer(instance=testRequest, data=data)
My solution was that i removed the depth value from the serializer for the POST requests and added a separate serializer with the depth value for the GET requests.
Related
I have a Django REST project. There is a model Product. I get some data from marketplace API about products stocks. And I need to save it to my datbase. I don't know, what kind of viewsets to choose. And how to make a create method. Thanks.
My Product model
`
class Product(models.Model):
store = models.ForeignKey(
Store,
on_delete=models.PROTECT, blank=True,
verbose_name="Store")
offer_id = models.CharField(max_length=50,
blank=True,
default="",
verbose_name="SKU")
name = models.CharField(max_length=128,
blank=True,
default="",
verbose_name="Name")
present = models.PositiveIntegerField(
default=0,
verbose_name="Present")
reserved = models.PositiveIntegerField(
default=0,
verbose_name="Reserved")
`
My serializer
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
store = serializers.CharField()
offer_id = serializers.CharField()
name = serializers.CharField()
present = serializers.IntegerField()
reserved = serializers.IntegerField()
The data I get from API is a list, for example:
[
{
"offer_id":"1-100-3-0",
"present":5,
"reserved":1
},
{
"offer_id":"1-101-3-9",
"present":0,
"reserved":0
}
]
Refer the doccumentation for more
here is how you do it
#api_view([ 'POST'])
def product_post(request):
if request.method == 'POST':
serializer = ProductSerializer(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)
and urls should be
path('product_post/', views.product_post, name='product_post'),
I creat a Modelviewset, it works with out errors, but stocks aren't saved in DB.
I tried to create and run a test, but I have
self.assertEqual(len(Product.objects.filter(store__user_id=self.user.pk)), 1)
AssertionError: 0 != 1
So, I can't find a reason.
class ProductApi(ModelViewSet):
serializer_class = ProductSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return Product.objects.filter(
store__user_id=self.request.user.pk)\
.order_by('-offer_id')
def create(self, request, *args, **kwargs):
st = Store.objects.filter(user=self.request.user,
type=1)
for _ in st:
api = Parser(api_key=_.api_key,
client_id=_.client_id)
data = api.get_product_info_stocks()
if len(data) > 0:
for stock in data:
if Product.objects.filter(
offer_id=stock.get('offer_id')).exists():
pass
else:
stock_data = {
'offer_id': stock['offer_id'],
'present': stock['present'],
'reserved': stock['reserved']
}
Product.objects.create(**stock_data)
stocks = Product.objects.filter(
store__in=st).order_by(
'-offer_id')
s = ProductSerializer(stocks, many=True)
return Response(status=200, data=s.data)
else:
return Response(status=204,
data={"Info": "Empty response"})
return Response(status=400,
data={"Message": "User has no stores"})
I have this model in Django:
class Post(models.Model):
poster = models.ForeignKey('User', on_delete=models.CASCADE, related_name='posts')
body = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
likers = models.ManyToManyField('User', blank=True, null=True, related_name='liked_posts')
likes = models.IntegerField(default=0)
def serialize(self):
return {
'id': self.pk,
'poster': self.poster.username,
'body': self.body,
'timestamp': self.timestamp.strftime('%b %d %Y, %I:%M %p'),
'likes': self.likes
}
It works but when I try to add likers to it, I get an error which says I can't use manytomany fields. How can I do such thing?
I fetch it in JavaScript like this:
fetch('/posts')
.then(response => response.json())
.then(data => {
console.log(data);
});
Using this view:
def posts_view(request):
posts = Post.objects.all()
posts = posts.order_by('-timestamp').all()
return JsonResponse([post.serialize() for post in posts], safe=False)
You don't have to serialize your models manually, i.e. defining serialize(self) method for each model. Try to use django-rest-framework, everything is already made for you.
Define serializers for your models in serializers.py.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
poster = UserSerializer()
likers = UserSerializer(many=True, allow_null=True, default=None)
# if you want short info about likers (list of ids), use snippet below
# likers = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), many=True)
class Meta:
model = Post
fields = "__all__"
And then in your view:
def posts_view(request):
posts = Post.objects.all().order_by('-timestamp')
data = PostSerializer(posts, many=True).data
return JsonResponse(data)
I am trying get the user names instead of user id, in the created_by field of the Comment class. My model is below:
class Comment(models.Model):
thread_id = models.ForeignKey(Thread, on_delete=models.CASCADE)
content = models.TextField()
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
likes = models.IntegerField(default=0)
Have used slugfield in the serializer.
class CommentSerializer(serializers.ModelSerializer):
created_by = serializers.SlugRelatedField(
slug_field='email',
queryset=User.objects.all())
class Meta:
model = Comment
fields = ("id", "thread_id", "content", "created_by", "created_at", "likes")
Where as in the api below, the field created_by still has user id and not email.
#action(detail=True, methods=["GET"])
def list_comments(self, request, pk):
comments = Comment.objects.filter(
thread_id = pk
)
data = json.loads(serialize('json', comments))
print("**************data**************")
print(data)
return Response(data, status.HTTP_200_OK)
The data printed looks like this :
[{'model': 'blog.comment', 'pk': 20, 'fields': {'thread_id': 19, 'content': 'hi', 'created_by': 2, 'created_at': '2021-07-14T03:34:11.333Z', 'likes': 0}}]
Is this because of serialize? How do I correctly get the value of the created_by as user email and not id ?
Use to_representation() method on the serializer
def to_representation(self, instance):
rep = super(CommentSerializer, self).to_representation(instance)
rep['created_by'] = instance.created_by.email
return rep
I am new to Django REST, I was trying to make some entry to the DB using the serilaizer in django rest. But i am getting some errors while using the create method.
My models are,
class CoreInformation(models.Model):
site_name = models.CharField(max_length=145, blank=True, null=True)
status = models.CharField(max_length=45, blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
class Meta:
managed = False
db_table = 'core_information'
class CoreDetailInformation(models.Model):
core_info = models.ForeignKey('CoreInformation', models.DO_NOTHING, related_name='core_base_info')
old_sac = models.CharField(max_length=45, blank=True, null=True)
msc = models.CharField(max_length=45, blank=True, null=True)
class Meta:
db_table = 'core_detail_information'
And i have two ModelSerializer like below ,
class CoreDetailSerializer(serializers.ModelSerializer):
class Meta:
model = CoreDetailInformation
fields = ('id','old_sac', 'msc')
class CustomCoreInfoSerializer(serializers.ModelSerializer):
core_base_info = CoreDetailSerializer(many=True)
class Meta:
model = CoreInformation
# fields = '__all__'
fields = ('id', 'site_name', 'status', 'created_at', 'core_base_info')
#transaction.atomic
def create(self, validated_data):
try:
with transaction.atomic():
base_info = CoreInformation.objects.create(site_name=validated_data['site_name'],status=validated_data['status']
for site_detail in validated_data['core_base_info']:
CoreDetailInformation.objects.get_or_create(msc=site_detail['msc'],old_sac=site_detail['old_sac'],core_info=base_info)
except CoreInformation.DoesNotExist as e:
raise e
except CoreDetailInformation.DoesNotExist as e:
raise e
and my views.py is ,
class CoreInformation(generics.ListCreateAPIView):
queryset = models.CoreInformation.objects.all()
serializer_class = CustomCoreInfoSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
instance = self.perform_create(serializer)
serializer = self.get_serializer(instance=instance)
return Response(serializer.data, status=status.HTTP_201_CREATED)
def perform_create(self, serializer):
return serializer.create(validated_data=serializer.validated_data)
To create the CoreInformation my input will be like below,
{
"site_name": "xxxxxxxxxx",
"status": "create",
"core_base_info": [{
"old_sac": '1',
"msc": "abc1,abc2"
},
{
"old_sac": '2',
"msc": "abc3,abc4"
}]
}
But when i am compiling its returns me the below error,
AssertionError at /api/core/
The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `src.core.serializers.CustomCoreInfoSerializer`, or set `read_only=True` on nested serializer fields.
I found this , but did n't help for me.
Any help would be greatly appreciated. Thanks.
I think that you can use this GitHub to solve your problem
Try this: https://github.com/beda-software/drf-writable-nested
My model relationships are: A 'Reader' will have a single 'Wishlist' and a 'Wishlist' will contain many 'Book's. I want to create an empty Wishlist automatically during Reader object instance creation.
My Models:
class Wishlist(models.Model):
wishlist_id = models.AutoField(primary_key=True)
class Reader(models.Model):
user = models.OneToOneField(User)
phone = models.CharField(max_length=30)
address = models.CharField(max_length=80)
dob = models.DateField(auto_now=False, auto_now_add=False, null=True, blank=True)
# A library has many readers
which_library = models.ForeignKey('Library', related_name='readers', on_delete=models.CASCADE)
wishlist = models.OneToOneField(Wishlist, null=True, blank=True)
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=30)
which_wishlist = models.ForeignKey('Wishlist', related_name='my_wishlist', on_delete=models.CASCADE, null=True, blank=True)
And serializer:
class ReaderSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email')
password = serializers.CharField(source='user.password')
class Meta:
model = Reader
#fields = '__all__'
#depth = 1
fields = ('id', 'username', 'email', 'password', 'phone', 'address', 'dob', 'which_library', 'wishlist')
def update(self, instance, validated_data):
instance.user.email = validated_data.get('user.email', instance.user.email)
instance.user.password = validated_data.get('user.password', instance.user.password)
instance.phone = validated_data.get('phone', instance.phone)
instance.address = validated_data.get('address', instance.address)
instance.dob = validated_data.get('dob', instance.dob)
instance.which_library = validated_data.get('which_library', instance.which_library)
instance.wishlist = validated_data.get('wishlist', instance.wishlist)
instance.save()
return instance
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.set_password(user_data['password'])
user.save()
wishlist_data = validated_data.pop('wishlist')
reader = Reader.objects.create(**validated_data)
Wishlist.objects.create(reader=reader, **wishlist_data)
return reader
My view that handles the creation:
#api_view(['GET', 'POST'])
def reader(request, library_id):
"""
List all readers in a specific library, or create a new
"""
if request.method == 'GET':
...
elif request.method == 'POST':
serializer = ReaderSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I am making a POST call to create a Reader with following JSON data:
{
"username": "sample",
"email": "sample#gmail.com",
"password": "012345",
"phone": "012345",
"address": "sample address",
"which_library": "2",
"wishlist": []
}
Where it gives me following error:
{
"wishlist": [
"Incorrect type. Expected pk value, received list."
]
}
What am I doing wrong?
I would throw away the Wishlist model completely as it does not hold any data.
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=30)
reader = models.ManyToManyField('Reader', related_name='wishlist')
This should make the whole thing easier as you no longer need to create the wishlist automagically and I guess your problem will desolve in air.
If you need the Model (e.g. you expect data to arise later) you might use through on the ManyToManyField. Details can be found in the docs
UPDATE
I rethought the advise regarding the through model. This approach would not reflect your current state as it would have one entry per relation not per reader. Nevertheless I would recommend the simplification of your models as suggested above.