I am creating a post function to save data in a model, Here's the code:
Views.py
class take_quizapi(CreateAPIView):
def post(self, request,pk, *args, **kwargs):
supplier = request.user.supplier
data = request.data.copy()
data["supplier_id"] = supplier.user_id
data["score"] = 0
data["quiz"] = pk
print("data is", data)
serializer = takenquizSerializer(data=data)
if serializer.is_valid():
serializer.save()
print("Serializer data", serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
when I call the API from postman it says:
django.db.utils.IntegrityError: null value in column "supplier_id" violates not-null constraint
But I am clearly providing the data["supplier_id"] = supplier.user_id.
What is it that I am doing wrong here ?
Serializers.py
class takenquizSerializer(serializers.ModelSerializer):
supplier = ReadOnlyField(source='supplier.supplier_fname')
class Meta:
model = TakenQuiz
fields = "__all__"
Models.py
class TakenQuiz(models.Model):
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, related_name='taken_quizzes')
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='taken_quizzes')
score = models.FloatField()
date = models.DateTimeField(auto_now_add=True)
least_bid = models.IntegerField(default=0)
confirmed = models.CharField(max_length=100, default='Not Confirmed')
supplier is a read only field. So there's no point in adding it to the data dict, since it will never be used.
You shouldn't be modifying the post data anyway - there's a reason that is immutable. Instead pass it in when saving:
serializer = takenquizSerializer(data=request.data)
if serializer.is_valid():
serializer.save(supplier_id=supplier.user_id, score=0, quiz=pk)
Related
When I post to array json like {"productId":[1, 2, 3]}.
I got errors
Cannot assign "[<Product: Short Sleeve>, <Product: Short Sleeve>, <Product: Short Sleeve>]": "FittingCartItem.productId" must be a "Product" instance.
I already try add many=True argument in get_serializer function.
I don't know how solve this problem...
serializers.py
class ItemListSerializer(serializers.ModelSerializer):
product = ProductSerializer(source='productId', read_only=True)
memberId = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), write_only=True, required=False)
productId = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all(), write_only=True, many=True)
is_live = serializers.BooleanField(default=True)
class Meta:
model = FittingCartItem
fields = '__all__'
views.py
class ItemListView(generics.ListCreateAPIView):
serializer_class = ItemListSerializer
queryest= FittingCartItem.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
class FittingCartItem(models.Model):
memberId = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='fittingcarts')
productId = models.ForeignKey(Product, on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True)
is_live = models.BooleanField(default=True)
In yout serializer, you want your product to be serialized by ProductSerializer, which causes your serializer to expect "Product" instances instead of productIds in your JSON input.
What you need is to tell yout serializer to expect products as productIds, which is possible by using PrimaryKeyRelatedField.
You need to delete:
product = ProductSerializer(source='productId', read_only=True)
This will solve your problem because DRF automatically acts ForeignKeys in models as PrimaryKeyRrlatedFields. But if productId was not defined as ForeignKey, but instead ManyToMany relation etc. You need to add it as below:
productId = PrimaryKeyRelatedField(many=True, read_only=True)
I am trying to add a request.user to a manytomany field to create an item. But I receive this error:
TypeError at /api/v1/movies/
'User' object is not iterable
Models.py
class Movie(models.Model):
class Movie(models.Model):
title = models.CharField(max_length=100)
genre = models.CharField(max_length=100)
year = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
owners = models.ManyToManyField('auth.User', related_name='movies')
views.py
# Create a new movie
def post(self, request):
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owners=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Per comment and updated question, the desired behavior is multiple users. Assuming that your MovieSerializer is a subclass of ModelSerializer and that you have used the default (PrimaryKeyRelatedField) binding for the owners serializer field, you should be able to add the requestor in the initial data:
def post(self, request):
serializer = MovieSerializer(data=dict(request.data, owners=[request.user.id]))
if serializer.is_valid():
serializer.save()
...
NOTE: since owners is a ManyToMany relation, you need to stick the user in an array, and for serialization this needs to be the pk of the user, not the object.
I am new to django-rest-framework. I am building an employee scheduling application where I have a REST Api built with drf and frontend in angular. Below is one of my models and it's corrsponding serializer and viewset.
model:
class Eventdetail(models.Model):
event = models.ForeignKey(Event, models.DO_NOTHING, blank=True, null=True)
start = models.DateTimeField(blank=True, null=True)
end = models.DateTimeField(blank=True, null=True)
employee = models.ForeignKey(Employee, models.DO_NOTHING, blank=True, null=True)
location = models.ForeignKey(Location, models.DO_NOTHING, blank=True, null=True)
is_daily_detail = models.BooleanField
def __str__(self):
return self.event
serializer:
class LocationTrackSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
many = kwargs.pop('many', True)
super(LocationTrackSerializer, self).__init__(many=many, *args, **kwargs)
location = serializers.SlugRelatedField(slug_field='location_name', queryset=Location.objects.all())
location_color = serializers.CharField(source='location.location_color', read_only=True)
class Meta:
model = Eventdetail
fields = ('id','employee','location','location_color','start','end')
viewset:
class LocationTrackViewSet(viewsets.ModelViewSet):
queryset = Eventdetail.objects.all()
serializer_class = LocationTrackSerializer
def create(self, request, *args, **kwargs):
self.user = request.user
listOfThings = request.data['events']
serializer = self.get_serializer(data=listOfThings, many=True)
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
As you can see, this exposes event details of all employees. Now when new events are posted, I want to be able to find if the start and end times of posted events overlap with existing events and throw a warning message with info of overlapping events after creation. I still want to allow save but only return warnings after save. i am trying to figure out a way to do this. I looked at how to create validators, but I am not sure if that is how I should go about this. Any help is appreciated! Thanks.
You can add a field warning_message to the serializer as follows -
class LocationTrackSerializer(serializers.ModelSerializer):
# rest of the code
def get_warning_message(self, obj):
warning_msg = ''
# logic for checking overlapping dates
# create a method `are_dates_overlapping` which takes
# start and end date of the current obj and checks with all
# others in queryset.
overlap = are_dates_overlapping(obj.start, obj.end)
if overlap:
warning_msg = 'overlaps'
return warning_msg
warning_message = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Eventdetail
fields = ('id','employee','location','location_color','start','end', 'warning_message')
Ref: Serializer Method Field in DRF
I have a serializer that works fine for the GET, POST, DELETE actions. It exposes the model fields that I want. However for the PUT action, the user will send back values that aren't built into my models and the server will deal with how to perform the update on the model. I can send the data back using Postman or Curl and it works but the browseable API still looks like this:
For the PUT method I want "is_winner", "num_hands_won", and "score" to show up instead of the actual model fields. How do I do this? (Let me know in the comments if you need more info)
StatisticsSerializer:
class StatisticsSerializer(serializers.ModelSerializer):
# pk = serializers.IntegerField(required=False)
class Meta:
model = Statistics
fields = [
'url',
'games_won',
'hands_won',
'games_played',
'high_score',
'low_score',
]
Statistics Model:
class Statistics(models.Model):
# Define model fields:
user = models.OneToOneField(User, primary_key=True)
games_won = models.IntegerField(null=True, blank=True)
hands_won = models.IntegerField(null=True, blank=True)
games_played = models.IntegerField(null=True, blank=True)
high_score = models.IntegerField(null=True, blank=True)
low_score = models.IntegerField(null=True, blank=True)
def __str__(self):
return str(self.pk)
def increment_games_won(self, is_winner):
if is_winner is True:
self.games_won = self.games_won + 1
return self.games_won
def add_to_hands_won(self, num_hands_won):
if num_hands_won > 0 and num_hands_won < 8:
self.hands_won = self.hands_won + num_hands_won
return self.hands_won
def increment_games_played(self):
self.games_played = self.games_played + 1
return self.games_played
def new_high_score(self, score):
if score > self.high_score:
self.high_score = score
return self.high_score
def new_low_score(self, score):
if score < self.low_score:
self.low_score = score
return self.low_score
Statistics ViewSet:
class StatisticsViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Statistics.objects.all()
serializer_class = StatisticsSerializer
filter_class = StatisticsFilter
search_fields = ('pk', 'user')
ordering_fields = ('games_won', 'hands_won', 'games_played', 'high_score', 'low_score')
def update(self, request, pk=None):
stats = self.get_object()
stats.increment_games_won(request.data['is_winner'])
stats.add_to_hands_won(request.data['num_hands_won'])
stats.increment_games_played()
stats.new_low_score(request.data['score'])
stats.new_high_score(request.data['score'])
stats.save()
serialized_stats = StatisticsSerializer(stats, context={'request': request}).data
return Response(serialized_stats)
You could probably use another Serializer and use it for you PUT API
StatisticsUpdateSerializer:
class StatisticsUpdateSerializer:
is_winner = ...
num_hands_won = ...
score = ...
And use this serializer in the PUT API or create a new route as shown in the example mentioned in the DRF documentation here
#detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
// Use your serializer below
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
So I have a view that accepts serialized Album object and assign the owner to current user
class AlbumListViewer(APIView):
def post(self, request, format = None):
request.data['user_id'] = request.user.id
serializer = AlbumSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(status.HTTP_201_CREATED)
return Response(status = status.HTTP_400_BAD_REQUEST)
The error I get is
null value in column "user_id" violates not-null constraint
My Serializer looks like this
class AlbumSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Album
fields = ('id','album_name', 'date_created','user')
And finally my Model looks like this
class Album(models.Model):
user = models.ForeignKey(User)
album_name = models.CharField(max_length=30)
date_created = models.DateTimeField(editable=False, auto_now_add=True)
I have tried assigning a User ID in the JSON data but it is not being recognized by the serializer, anyway to assign this serialized object and give it a owner before saving?
Your have some problems in your AlbumListViewer. Do not try to add to the validatated_data anything. Instead pass on partial=True to the serializer, and after validation, use the save() method to save the missing values.
class AlbumListViewer(APIView):
def post(self, request, format = None):
serializer = AlbumSerializer(data=request.data, partial=True)
if serializer.is_valid():
user = request.user
serializer.save(user=user)
return Response(status=status.HTTP_201_CREATED)
return Response(status = status.HTTP_400_BAD_REQUEST)