perform_create saving user data - django

I have EndPoint that let user booking halls. But when i try to booking hall it give me error NOT NULL constraint failed. I lock at the problem 'NOT NULL constraint failed' after adding to models.py. they suggest to put null in the filed. But in my case i must save the hall id otherwise I can not know the hall that user is booking (it save null in the hall)
at the beginning it show this Error: NOT NULL constraint failed: api_bookingmodel.user_id and I solve it by putting in the function perform_create ... serializer.save(user=self.request.user)
This is my Code....
Model classes
class HallModel(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='hall_owner')
name = models.CharField(max_length=100)
price = models.FloatField()
phone = models.CharField(max_length=9, blank=True)
size = models.CharField(max_length=50)
region = models.CharField(max_length=100)
state = models.CharField(max_length=100)
image_1 = models.ImageField(upload_to='halls_image')
image_2 = models.ImageField(upload_to='halls_image')
image_3 = models.ImageField(upload_to='halls_image')
created_at = models.DateTimeField(auto_now_add=True)
class BookingModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_booking')
hall = models.ForeignKey(HallModel, on_delete=models.CASCADE, related_name='hall_owner')
time_date = models.DateTimeField(auto_now_add=True)
booking_method = models.IntegerField()
Serializer
class HallSerializer(serializers.ModelSerializer):
booking = serializers.SerializerMethodField()
class Meta:
model = HallModel
fields = '__all__'
def get_booking(self, obj):
booking = BookingSerializer(obj.hall_owner.all(),many=True).data
return booking
def perform_create(self, serializer):
serializer.save()
class BookingSerializer(serializers.ModelSerializer):
hall = serializers.SerializerMethodField()
class Meta:
model = BookingModel
fields = '__all__'
depth = 1
def get_hall(self, obj):
serializer_data = HallSerializer(obj.hall).data
return serializer_data
View Sets
class HallViewSet(viewsets.ModelViewSet):
queryset = HallModel.objects.all()
serializer_class = HallSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class BookingViewSet(viewsets.ModelViewSet):
serializer_class = BookingSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_queryset(self):
return self.request.user.user_booking.all()
it show this Error: IntegrityError at /api/booking/
NOT NULL constraint failed: api_bookingmodel.hall_id... I think i must save the hall id in the perform_create function but I don't able to do it. the stange things is when i delete the depth = 1 in the booking serializer it not show me the Error ... any one have a solution.

You can override def perform_create in BookingViewSet class, like below:
def perform_create(self, serializer):
serializer.save(
user=self.request.user,
hall=self.request.data['hall_id']
)
make sure that hall_id is ready in post request. Also def perform_create in HallSerializer is useless. read cdrf.co

Related

DRF- Got assertion error when I give Post Request

Error
AssertionError: The `.create()` method does not support writable dotted-source fields by default.
Write an explicit `.create()` method for serializer `hrm_apps.configuration.serializers.CurrencySerializer`, or set `read_only=True` on dotted-source serializer fields.
models.py,
class CurrencyMaster(models.Model):
code = models.CharField(max_length=3, null=False, unique=True)
name = models.CharField(max_length=100, null=False, unique=True)
def __str__(self):
return self.name
class Currency(models.Model):
currency_master = models.OneToOneField(CurrencyMaster, on_delete=models.RESTRICT)
conversion_rate = models.FloatField(null=False)
def __str__(self):
return self.currency_master.name
views.py,
class CurrencyViewSet(viewsets.ModelViewSet):
queryset = Currency.objects.all()
serializer_class = CurrencySerializer
lookup_field = 'id'
serializers.py,
class CurrencySerializer(serializers.ModelSerializer):
currency_master = serializers.CharField(source="currency_master.name")
class Meta:
model = Currency
fields = ['id', 'currency_master', 'conversion_rate']
When i give post request i got assertion error like above,
class CurrencySerializer(serializers.ModelSerializer):
currency_master = serializers.CharField(source="currency_master.name")
class Meta:
model = Currency
fields = ['id', 'currency_master', 'conversion_rate']
def create(self, validated_data):
return Currency.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.currency_master = validated_data.get('currency_master', instance.currency_master)
instance.conversion_rate = validated_data.get('conversion_rate', instance.conversion_rate)
return instance
I tried above i got this error "ValueError: Cannot assign "{'name': 'ALL - Albania Lek'}": "Currency.currency_master" must be a "CurrencyMaster" instance". How to resolve this???
Customize the create method.
class CurrencySerializer(serializers.ModelSerializer):
currency_master = serializers.CharField(source="currency_master.name")
class Meta:
model = Currency
fields = ['id', 'currency_master', 'conversion_rate']
def create(self, validated_data):
currency_master_name = validated_data.pop('currency_master')["name"]
currency_master_obj = get_object_or_404(CurrencyMaster, name=currency_master_name)
validated_data["currency_master"] = currency_master_obj
return Currency.objects.create(**validated_data)

POST data only if you are authenticated and only to your user using django-rest-framework

i'm new to Django so sorry if this seems stupid.
i want to add an item to a database only if the user is authenticated .
here are the models:
class SaleItems(models.Model):
product_name = models.CharField(max_length=50)
price = models.IntegerField()
product_type = models.CharField(max_length=25)
description = models.CharField(max_length=250 ,default='', blank=True)
brand = models.CharField(max_length=25, null=True,blank=True)
image_path = models.ImageField(upload_to='images/product_image')
date_added = models.DateField(auto_now_add=True)
in_stock = models.BooleanField(default=True)
def __str__(self):
return f"{self.product_name}, price={self.price}"
class SaleHistory(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(SaleItems, on_delete=models.RESTRICT, default=None)
date_bought = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.date_bought}, {self.product}, {self.user}'
the serializers:
class SaleItemSerializer(serializers.ModelSerializer):
class Meta:
model = SaleItems
fields = '__all__'
class SaleHistorySerializier(serializers.ModelSerializer):
class Meta:
model = SaleHistory
fields = '__all__'
urls:
routes = routers.DefaultRouter()
routes.register('api/saleitems', SaleItemViewSet, basename='saleitem')
routes.register('api/salehistory', SaleHistoryViewSet, basename='salehistory')
urlpatterns = [ path('',include(routes.urls))
]
and finally the apis
class SaleItemViewSet(viewsets.ModelViewSet):
queryset = SaleItems.objects.all()
permission_classes = [permissions.AllowAny]
serializer_class = SaleItemSerializer
class SaleHistoryViewSet(viewsets.ModelViewSet):
# queryset = SaleHistory.objects.all()
permission_classes = [permissions.IsAuthenticated]
serializer_class = SaleHistorySerializier
def get_queryset(self):
user = self.request.user
return SaleHistory.objects.filter(user = user)
so the problem, when i post to 'api/salehistory' i am able to add content to any user not only as the authenticated user.
(using knox authtoken for authentication).
Say for example i am authenticated as user1 and i have my auth token. Now I can use that token to add items to the SaleHistory model for any user which is very much undesired.
how can i solve this?
once again sorry for the crude description. first time asking question here.
First, set the user field as read_only by using read_only_fields meta option
class SaleHistorySerializier(serializers.ModelSerializer):
class Meta:
model = SaleHistory
fields = '__all__'
read_only_fields = ("user",)
Now, the SaleHistorySerializier will not accept the data from the user field.
Then, You need to override the perform_create(...) method of the SaleHistoryViewSet class
class SaleHistoryViewSet(viewsets.ModelViewSet):
# queryset = SaleHistory.objects.all()
permission_classes = [permissions.IsAuthenticated]
serializer_class = SaleHistorySerializier
def get_queryset(self):
return SaleHistory.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)

Django Rest Framework - How to set the current_user when POST

I have just followed a small tutorial using DRF, but I can't figure how to like my model to his user when POSTing a new object
this is my model :
# Create your models here.
class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
#######
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
and so my serializer
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ("id", "title", "subtitle", "user_id")
so, now in the view I have access to the current_user with this :
request.user
class ListProjectsView(generics.ListCreateAPIView):
authentication_classes = [authentication.TokenAuthentication]
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def list(self, request):
queryset = Project.objects.filter(user_id=request.user.id)
serializer = ProjectSerializer(queryset, many=True)
return Response(serializer.data)
[...]
def create(self, request, pk = None):
return super(ListProjectsView, self).create(request, pk = None)
I suppose there is a way to passe the request.user is the create in order to allow my Project.user_id to be filled ?
Whatever I'm doing, I can't manage to set the user, and i get the
django.db.utils.IntegrityError: null value in column "user_id" violates not-null constraint error
Any idea?
Thanks!
Try to override with following method. Everytime PUT/PATCH/CREATE operation is performed following method is called. This is the good way to pass the current user.
def perform_create(self, serializer):
serializer.save(user = self.request.user)
class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
okay so you have FK user but try to access it with the user_id = request.user.id
Just call
queryset = Project.objects.filter(user=request.user)
queryset = Project.objects.filter (user_id=request.user.id)
if you want to match the id you should put two __
like so user__id = request.user.id but I dont see any sence making it.

Check the current values of instance while doing perform_update in django rest framework

I'm using Django Rest Framework.
Here is model:
class TimeSlot(models.Model):
date_start = models.DateTimeField(default=None, null=True, blank=True)
booked = models.BooleanField(default=False)
user = models.ForeignKey(User, related_name='time_slots', on_delete=models.SET_NULL, default=None)
Here is my serializer:
class CreateBookingSerializer(serializers.ModelSerializer):
class Meta:
model = TimeSlot
fields = ('id','booked','user',)
extra_kwargs = {'booked': {'required': True}}
Here is my view:
class CreateBookingViewSet(viewsets.ModelViewSet):
queryset = TimeSlot.objects.all()
serializer_class = CreateBookingSerializer
http_method_names = ['put']
def perform_update(self, serializer):
if (self.request.data['booked'] == 1):
serializer.save(user=self.request.user, booked=True)
I want to set booked to true only if the current value is False.
I'm using PUT method. so my url is /create_booking/id/
If I could get the id in my view, I would check the current booked value! How to get the id?
You can try to use self.kwargs['id'].
class CreateBookingViewSet(viewsets.ModelViewSet):
queryset = TimeSlot.objects.all()
serializer_class = CreateBookingSerializer
http_method_names = ['put']
def perform_update(self, serializer):
id = self.kwargs['id']
if (self.request.data['booked'] == 1):
serializer.save(user=self.request.user, booked=True)

Trouble POSTing multiple related objects at once to api created with django rest framework

I'm making a ToDo app but having difficulties getting the api to allow a user to create a new list with multiple items via one api call. Each list belongs to a specific "room".
I get 400 Bad Request. If I leave the 'todo_items' off the POST data it works fine to create the ToDoList object.
Also, if I remove "user" from the Meta fields attribute for the CreateToDoItemSerializer, it'll create both the ToDoList object and the ToDoItem objects, but the "content" for each ToDoItem will be an empty string. Inside the create method of NewToDoListSerializer, the validated_data is returning a list of empty OrderedDict() objects for the key "todo_items". I'm not sure what to make of that.
my models:
class Room(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
slug = models.SlugField(max_length=255, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="rooms")
class ToDoList(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, blank=True)
room = models.ForeignKey(Room, related_name="todo_lists")
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="todo_lists")
class ToDoItem(models.Model):
todo_list = models.ForeignKey(ToDoList, related_name="todo_items")
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="replies")
content = FroalaField(options={'placeholder': '''Just start writing...
Highlight any text to bring up the editor.'''})
my serializers
class CreateTodoItemSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(many=False, read_only=False, queryset=User.objects.all())
class Meta:
model = ToDoItem
fields = ['pk', 'user', 'content']
def create(self, validated_data):
reply = ToDo.objects.create(**validated_data)
class NewToDoListSerializer(serializers.ModelSerializer):
room = serializers.PrimaryKeyRelatedField(many=False, read_only=False, queryset=Room.objects.all())
user = UserSerializer(read_only=True)
todo_items = CreateTodoItemSerializer(many=True, read_only=False)
class Meta:
model = ToDoList
fields = ['pk', 'slug', 'title', 'user', 'room', 'todo_items']
lookup_field = "slug"
depth = 1
def create(self, validated_data):
todo_items_data = validated_data.pop('todo_items')
todo_list = ToDoList.objects.create(**validated_data)
for todo_item_data in todo_items_data:
todo_item = ToDo.objects.create(user=todo_list.user, todo_list=todo_list, **todo_item_data)
my viewset (the relevant bits):
class ToDoListViewSet(viewsets.ModelViewSet):
queryset = ToDoList.objects.all()
serializer_class = ToDoListSerializer
authentication_classes = [TokenAuthentication, SessionAuthentication]
permission_classes = [IsAuthenticated]
renderer_classes = (renderers.TemplateHTMLRenderer, renderers.JSONRenderer, renderers.BrowsableAPIRenderer)
template_name = "react_base.html"
lookup_field = "slug"
def create(self, request, **kwargs):
self.serializer_class = NewToDoListSerializer
return super(ToDoListViewSet, self).create(request, **kwargs)
def perform_create(self, serializer):
instance = serializer.save(user=self.request.user)
the data I'm POSTing:
todoListTitle, todoItemContent, moreTodoItemContent are all strings. this.props.room.pk is an integer. this.props.csrfmiddlewaretoken is the csrfmiddlewaretoken
var newToDoListData = {
"room": this.props.room.pk,
"title": todoListTitle,
"todo_items": [{"content": todoItemContent}, {"content": moreTodoItemContent}],
"csrfmiddlewaretoken": this.props.csrfmiddlewaretoken
};
You need to make the todo_items in your serializer required = false. I am not sure I understand what your second issue is.
class NewToDoListSerializer(serializers.ModelSerializer):
room = serializers.PrimaryKeyRelatedField(many=False, read_only=False, queryset=Room.objects.all())
user = UserSerializer(read_only=True)
todo_items = CreateTodoItemSerializer(many=True, required=False)
class Meta:
model = ToDoList
fields = ['pk', 'slug', 'title', 'user', 'room', 'todo_items']
lookup_field = "slug"
depth = 1
def create(self, validated_data):
todo_items_data = validated_data.pop('todo_items')
todo_list = ToDoList.objects.create(**validated_data)
for todo_item_data in todo_items_data:
todo_item = ToDo.objects.create(user=todo_list.user, todo_list=todo_list, **todo_item_data)