Django filtering ForeignKey - python

This is view:
def post(self, request):
author_request = request.data.get("author")
queryset = Book.objects.filter(author=author_request)
serializer = BookSerializer(queryset, None)
return Response(serializer.data, HTTP_200_OK)
This is model:
class Author(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
full_name = models.CharField(max_length=255, null=True, blank=True)
about = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.full_name
class Meta:
verbose_name = 'Author'
verbose_name_plural = 'Authors'
So, when i try to filter the book by author i get the error. The error tells me that the POST data that i entered which is "Aleksa Petrovic" a name of a author that exists in the database is not an UUID. So when i filter it, it filters by UUID and i want it to filter by "full_name"

You can .filter(…) [Django-doc] with:
Book.objects.filter(author__full_name=name_of_author)
One can use double underscores (__) to look "through" relations.
The view thus looks like:
def post(self, request):
author_request = request.data.get('author')
queryset = Book.objects.filter(author__full_name=author_request)
serializer = BookSerializer(queryset, None)
return Response(serializer.data, HTTP_200_OK)
Typically searches with a filter are not handled by a POST request, but by a GET request, since a GET request is supposed to retrieve data from the web server.

Related

Why is PUT request not updating the Django rest framework database?

I am trying to update the database using a PUT request. Currently, I am able to update the database from the Django Admin successfully but I want to do same using a PUT request.
When ever I make a PUT request, I get a 200 OK response with no errors but the data is not updating in the database. I dont know why. I am confused. Someone please help me. Thank you.
models.py
class User_Order(models.Model):
order = models.OneToOneField(Orders, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
id = models.IntegerField(primary_key=True)
shirts = models.IntegerField(default=0)
shorts = models.IntegerField(default=0)
trousers = models.IntegerField(default=0)
total_units = models.CharField(max_length=2000, blank=True)
shirts_amount = models.CharField(max_length=2000, blank=True)
shorts_amount = models.CharField(max_length=2000, blank=True)
trousers_amount = models.CharField(max_length=2000, blank=True)
total = models.CharField(max_length=200,blank=True)
verified = models.BooleanField(null=True)
doing_laundry = models.BooleanField(null=True)
delivery_underway = models.BooleanField(null=True)
delivered = models.BooleanField(null=True)
address = models.TextField(default='')
time = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.user = self.order.user
self.id = self.order.id
self.shirts = self.order.shirts
self.shorts = self.order.shorts
self.trousers = self.order.trousers
self.total_units = self.order.total_units
self.shirts_amount = self.order.shirts_amount
self.shorts_amount = self.order.shorts_amount
self.trousers_amount = self.order.trousers_amount
self.total = self.order.total
self.verified = self.order.verified
self.doing_laundry = self.order.doing_laundry
self.delivery_underway = self.order.delivery_underway
self.delivered = self.order.delivered
self.address = self.order.address
super().save(*args, **kwargs)
def __str__(self):
return f'{self.order.user.username} Order'
serializers.py
class UserUser_OrderSerializer(serializers.ModelSerializer):
class Meta:
model = User_Order
fields = '__all__'
views.py
#api_view(['PUT'])
def UserOrdersUpdate(request, pk):
permission_classes = [IsAuthenticated]
user_order = User_Order.objects.get(id=pk)
if request.method == 'PUT':
serializer = UserUser_OrderSerializer(instance=user_order, data=request.data, many = False, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
Maybe it's because in User_Order.save() method you replace all fields from User_Order where all the data from serializer is stored with Order instance values.
Also you can use class based view UpdateAPIView to simplify it:
class UserOrdersUpdateView(generics.ListCreateAPIView):
queryset = User_Order.objects.all()
serializer_class = UserUser_OrderSerializer
permission_classes = [IsAuthenticated]
Docs:
UpdateAPIView
Used for update-only endpoints for a single model instance.
Provides put and patch method handlers.
Extends: GenericAPIView, UpdateModelMixin.

django Select_related(): from another model has a ForginKey To the model that will execute Select_related

I have two model, one for Question and two for answer
tow have a forginkey to one and one have a forginkey to users
for this purpose I use a To be grouped together at the same endpoint I add the second to the first in serializer
With this solution, Django accessed the database with the number of answers in the second form and I cannot use Select_related here
the question is, how can I reduce database hits to the second model
Models.py
class Consultation(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
create_at = models.DateTimeField(auto_now_add=True)
files = models.FileField(upload_to="media/files", null=True, blank=True,
validators=[FileExtensionValidator(['pdf', 'jpg', 'png'])])
def __str__(self):
return self.user.username
class Meta:
ordering = ['-create_at']
class ConsultaionAnswer(models.Model):
consultation = models.ForeignKey(
Consultation, on_delete=models.CASCADE, related_name='reply')
answer = models.TextField(null=True, blank=True)
def __str__(self):
return self.consultation.content[:20] + "..."
serializers.py
class ConsultaionAnswerSerilaizers(serializers.ModelSerializer):
class Meta:
model = ConsultaionAnswer
fields = ('answer',)
class ConsultationSerilaizers(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
username = serializers.StringRelatedField(
source='user.username',
read_only=True,
)
reply = ConsultaionAnswerSerilaizers(read_only=True, many=True)
class Meta:
model = Consultation
fields = (
"id",
"user",
"content",
'create_at',
'files',
'username',
"reply"
)
views.py
class ConsultationView(APIView):
serializer_class = ConsultationSerilaizers
def get(self, request, format=None):
consultat = Consultation.objects.select_related().filter(user=request.user)
serializer = self.serializer_class(
consultat, many=True, context={'request': request})
return Response(serializer.data)
django debug tool bar
This should works:
consultat = Consultation.objects.prefetch_related('reply').filter(user=request.user)
Since it is a one-to-many relation, you'll need a prefetch_related instead (see this link). And you also need to specify which field(s) you want to prefetch as well.
prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related,

django foreign key cannot be null

I am a beginner to django and trying to create a post request on django rest-framework.
I have a following model:
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
def __str__(self):
return str(self.name)
And a serializer:
class SchemeDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectScheme
fields = ('id', 'name', 'parent_scheme_id', 'rule')
depth=1
And my view:
#api_view(['POST'])
#renderer_classes((JSONRenderer,))
def create_project_scheme(request):
if request.method == 'POST':
data = JSONParser().parse(request)
serializer = SchemeDetailSerializer(data=data)
comp_logger.info('Call to create_project')
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response({'response':serializer.errors})
return Response({})
With post request body as:
{
"name": "django-rf"
}
This gives serializer.is_valid() to true, but in response I get
(1048, "Column 'parent_scheme_id_id' cannot be null")
I tried adding parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE, blank=False, null=False) but that didn't make any difference.
How can I validate the request input so that it shows proper validation message like for name field?
In your model, you set your ForeignKey field as a required field, Django by default consider a field required=True if not explicitely provide null=True. So if you want to create ProjectScheme instance without a ForeignKey referrence, then make sure you provide null=True to your ForeignKey field,
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, null=True, blank=True, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
def __str__(self):
return str(self.name)
in mention, blank=True works in form level validation.

Alllow one extra filed into serializer and return validated data with that field in Django Rest Framework

I know on this topic people asked a question before but my case is different and I have implemented almost all the solutions which I found but none of them are worked for me.
First I have defined three classes in models:
models.py
class User(AbstractBaseUser, PermissionsMixin):
""" User Model """
username = None
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
agency = models.ForeignKey('agency.Agency', on_delete=models.CASCADE, null=True)
weekly_email = models.NullBooleanField()
is_create_new_password = models.NullBooleanField(default=True)
is_active = models.BooleanField(default=True)
last_login_time = models.DateTimeField(null=True)
last_login_from = models.CharField(max_length=255, null=True)
created_at = models.DateField(default=timezone.now)
updated_at = models.DateField(default=timezone.now)
created_by = models.IntegerField(null=True)
updated_by = models.IntegerField(null=True)
""" The `USERNAME_FIELD` property tells us which field we will use to log in.
In this case, we want that to be the email field. """
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
""" Tells Django that the UserManager class defined above should manage
objects of this type. """
objects = UserManager()
class Role(models.Model):
""" Role Model """
name = models.CharField(max_length=255, unique=True)
class UserRole(models.Model):
""" User Role Model """
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
Then I have defined my serializer for user module:
serializers.py
class RegistrationSerializer(serializers.ModelSerializer):
""" Serializers registration requests and creates a new user. """
user_id = serializers.IntegerField(required=False)
email = serializers.EmailField(max_length=255)
name = serializers.CharField(max_length=255)
agency_id = serializers.IntegerField(source='agency.id', required=False)
role = serializers.CharField(source='role.name')
weekly_email = serializers.NullBooleanField()
last_login_time = serializers.DateTimeField(required=False)
last_login_from = serializers.CharField(max_length=255, required=False)
class Meta:
model = User
fields = (
'role', 'user_id', 'email', 'name', 'agency_id', 'weekly_email', 'last_login_time',
'last_login_from'
)
And At the end, I have defined my view file for user creation:
views.py
class UserCreateAPIView(APIView):
""" User create Api view class """
#Allow any user (authenticated or not) to hit this endpoint.
permission_classes = (IsAuthenticated,)
serializer_class = RegistrationSerializer
def post(self, request):
""" create user using following logic. """
request.data['user_id'] = request.user.id
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user)
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
Now when I run it everything works fine like user is created, role is created as per my expectations. My view, serializer and models excuted but at the end on this line:
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
I am facing error like,
AttributeError: Got AttributeError when attempting to get a value for field `role` on serializer `RegistrationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'role'.
I think you need to use ModelSerializer
class RegistrationSerializer(serializers.Serializer):
to
class RegistrationSerializer(serializers.ModelSerializer):

Object of type 'ListSerializer' is not JSON serializable

I want to get all customer data and responses and also remarks.
This is model.py
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer)
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
This is serializers.py
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = serializers.SerializerMethodField()
def get_responses(self, obj):
responses = Response.objects.filter(customer=obj)
return ResponseSerializer(responses, many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
This is services.py
def customer_information(company_id=1):
cus = Customer.objects.filter(remarks__company_id=company_id)
return CustomerInformationSerializer(cus, many=True).data
This is views.py
class CustomerInformationView(APIView):
def get(self, request):
company_id = request.GET.get('company_id', 1)
resp = {'data': customer_information(company_id)}
return Response(data=resp, status=status.HTTP_200_OK)
This is url.py
url(r'^customer/$', CustomerInformationView.as_view()),
I'm having this problem. How can I solve this. Kindly guide me.
get function in your view should return responses.data, insted of responsed.
SIDE NOTE
First, let me point you to a resource that I think is GREAT for anything dealing with Django REST Framework:
Classy Django REST Framework. It is a fantastic resource because you can easily dig right into the source code to see how you may or may not need to override default operations.
MY ANSWER
What I suggest is that instead of using the APIView, you use ListAPIView.
It would look something like this:
from rest_framework.generics import ListAPIView
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer, related_name='responses')
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = ResponseSerializer(many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
class CustomerInformationView(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerInformationSerializer
lookup_field = 'remarks__company'
Note the change that I made by adding related_name to the customer field on your Response model. See Django documentation for more information on related_name. In short, it adds responses as a field name on your Customer model so that you can travel backwards through that relationship.
This is not tested, but this should be a better strategy to do what you want without having to have a get_responses method, or a services.py.
Some there might be error because of missing "/" at the end of path like "event-api"=incorrect and "event-api/" correct. That worked for me. Hope you also have same problem.
Incorrect: path('event-api',views.event_view,name="event-view")
Correct: path('event-api/',views.event_view,name="event-view")

Categories