Django DRF serializers, how to add new field, fusion of two models? - django

I have two models, I have to make an endpoint where the results of two tables should appear in the json, which have a fongeringkey that joins them.
My code is the following:
models.py
class Property(models.Model):
address = models.CharField(max_length=120)
city = models.CharField(max_length=32)
price = models.BigIntegerField()
description = models.TextField(blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'property'
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
status = models.ForeignKey(Status, on_delete=models.CASCADE)
update_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'status_history'
views.py
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 1000
class PropertyListView(viewsets.ModelViewSet):
http_method_names = ['get', 'head']
serializer_class = PropertyListSerializer
queryset = Property.objects.all()
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'list':
return PropertyListSerializer
return PropertyListSerializer
def get_queryset(self):
queryset = Property.objects.all()
if self.request.GET.get('year'):
queryset = queryset.filter(year=self.request.GET.get('year'))
if self.request.GET.get('city'):
queryset = queryset.filter(city=self.request.GET.get('city'))
if self.request.GET.get('status'):
queryse = queryset.filter(statushistory__status=self.request.GET.get('status'))
else:
queryset = queryset.order_by('-year')
return queryset
def list(self, request):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
I can correctly filter the parameters, city, year(model Property) and above all status(model StatusHistory) with the following code snippet:
if self.request.GET.get('status'):
queryset = queryset.filter(statushistory__status=self.request.GET.get('status'))
My problem is how I show in my JSON Response the new field fusion of the two models statushistory__statustry adding the following in serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
statushistory_set =StatusHistoryListSerializer(many=True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'statushistory_set')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
Without any result.

I think you could set related_name attribute in the foreign key field.
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name = 'status_histories')
status = models.ForeignKey(Status, on_delete=models.CASCADE)
...
And in the PropertyListSerializer,
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
status_histories = StatusHistoryListSerializer(many=True, read_only = True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'status_histories')
You can also get property field as an object in the StatusHistory like the following:
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
property = PropertyListSerializer(read_only = True)
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')

Related

Get serializer data by foreign key

I need to pass data from EmployeeSerializer to VacationSerializer as nested json. This is my serializer.py:
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ('pk', 'first_name', 'surname', 'patronymic', 'birthday', 'email', 'position', 'phone_number')
class VacationSerializer(serializers.ModelSerializer):
class Meta:
model = Vacation
fields = ('pk', 'start_date', 'end_date', 'employee_id', 'employee')
and my models.py:
class Employee(models.Model):
first_name = models.CharField("Name", max_length=50)
surname = models.CharField("surname", max_length=50)
patronymic = models.CharField("patronymic", max_length=50)
birthday = models.DateField()
email = models.EmailField()
position = models.CharField("position", max_length=128)
phone_number = models.CharField("phone", max_length=12, null=True)
is_deleted = models.BooleanField(default=False)
def __str__(self):
return f'{self.surname} {self.first_name} {self.patronymic}'
class Vacation(models.Model):
start_date = models.DateField()
end_date = models.DateField()
employee_id = models.ForeignKey(Employee, related_name='employee', on_delete=models.PROTECT, default=-1)
def __str__(self):
return f'{self.start_date} - {self.end_date}'
#property
def date_diff(self):
return (self.end_date - self.start_date).days
in views.py I have this:
#api_view(['GET', 'POST'])
def vacations_list(request):
if request.method == 'GET':
data = Vacation.objects.all()
serializer = VacationSerializer(data, context={'request': request}, many=True)
return Response(serializer.data)
I've tried this:
class VacationSerializer(serializers.ModelSerializer):
employee = EmployeeSerializer(read_only=True, many=True)
class Meta:
model = Vacation
fields = ('pk', 'start_date', 'end_date', 'employee_id', 'employee')
but it doesn't show me even empty nested json, it shows me only VacationSerializer data.
I easily can access VacationSerializer from EmployeeSerializer using PrimaryKeySerializer or any other serializer and get nested json where VacationSerializer data is nested in EmployeeSerializer data. But I want it opposite - nested json of employee related to this vacation. How to achieve this?
That is because of the naming on your model. You need to serialize the 'employee_id' field:
class VacationSerializer(serializers.ModelSerializer):
employee_id = EmployeeSerializer()
class Meta:
model = Vacation
fields = ('pk', 'start_date', 'end_date', 'employee_id')

Django DRF get_queryset, how to join two models?

I have two models, I have to make an endpoint where the results of two tables should appear in the json, which have a fongeringkey that joins them.
My code is the following:
models.py
class Property(models.Model):
address = models.CharField(max_length=120)
city = models.CharField(max_length=32)
price = models.BigIntegerField()
description = models.TextField(blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'property'
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
status = models.ForeignKey(Status, on_delete=models.CASCADE)
update_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'status_history'
views.py
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 1000
class PropertyListView(viewsets.ModelViewSet):
http_method_names = ['get', 'head']
serializer_class = PropertyListSerializer
queryset = Property.objects.all()
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'list':
return PropertyListSerializer
return PropertyListSerializer
def get_queryset(self):
queryset = Property.objects.all()
if self.request.GET.get('year'):
queryset = queryset.filter(year=self.request.GET.get('year'))
if self.request.GET.get('city'):
queryset = queryset.filter(city=self.request.GET.get('city'))
if self.request.GET.get('status'):
#what would I do here?
pass
else:
queryset = queryset.order_by('-year')
return queryset
def list(self, request):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
I don't know how to filter by get with the parameters, city, year(model Property) and status(model StatusHistory).
You only need to filter based in the status of the history you can do the following:
if self.request.GET.get('status'):
queryset = queryset.filter(statushistory__status=self.request.GET.get('status'))

how to fix AttributeError: 'RelatedManager' object has no attribute 'id'

I have two nested models (task and Proposal) with a foreign key relationship, i've followed every necessary step but i'm getting an inegrity error
below is d err and codes
class JobPost(models.Model):
id= models.UUIDField(default=uuid.uuid4, primary_key=True,
editable=False)
user = models.ForeignKey(CustomUser, related_name='users',
on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class Proposal(models.Model):
user = models.OneToOneField(CustomUser, related_name="user", on_delete=models.CASCADE)
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
task = models.ForeignKey(JobPost, on_delete=models.CASCADE, related_name='proposal')
Serializer
1st serializer
class JobSerializer(serializers.ModelSerializer):
user = serializers.CharField(source='user.username', read_only=True )
user_id = serializers.CharField(source='user.id', read_only=True)
proposals = ProposalSerializer(many=True, read_only=True)
class Meta:
model = JobPost
fields = [
'user',
'user_id',
'id',
'proposals',
'etc'
]
2nd Serializer
class ProposalSerializer(serializers.ModelSerializer):
user = serializers.CharField(source='user.username',read_only=True)
class Meta:
model = Proposal
fields = [
'id',
'proposal_description',
'duration',
'bid',
]
def create(self, validated_data, instance=None):
if 'task' in validated_data:
task = validated_data.pop('task')
else:
task = JobPost.objects.create(**validated_data)
user_proposal = Proposal.objects.update_or_create(
task = task, defaults=validated_data
)
return user_proposal
APIVIEW
class ProposalAPIView(generics.CreateAPIView):
serializer_class = ProposalSerializer
look_up = 'id',
queryset = Proposal.objects.all()
permissions_classes = [permissions.IsAuthenticated]
2nd APIView
class CreateJobPost(generics.CreateAPIView):
serializer_class = JobSerializer
permissions_classes = [permissions.IsAuthenticated]
err msg
task = CustomUser.objects.get(pk=self.request.user.task.id)
AttributeError: 'RelatedManager' object has no attribute 'id'
can anyone pls help
You haven't mentioned your models. Try this.
task_id = models.PositiveIntegerField(blank=True,null=True)

NOT NULL constraint failed: products_order.user_id when trying to save serializer django rest frame work

django rest framework gives me this error when i am trying to perform a post request to create a new order even though i specified the usr value in my serializer.save method.
here is my view
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_update(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()
my models.py
payment_methods = ((1,'credit card'),(2, 'cash'),(3, 'paypal'))
class Product(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
image = models.ImageField(upload_to = 'images/', height_field=300, width_field=300)
price = models.IntegerField()
quantity = models.IntegerField()
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='products')
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Order(models.Model):
item = models.ForeignKey(Product, on_delete= models.CASCADE, related_name='orders')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
quantity = models.IntegerField()
payment_options = models.CharField(choices=payment_methods, max_length=50)
Delivery = models.CharField(max_length=200)
my serializers
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'title', 'description','image', 'price', 'quantity', 'seller', 'date']
read_only_fields = ['date', 'seller']
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
read_only_fields = ['user']
You need to use perform_create and not perform_update in your view.
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_create(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()

Django rest "this field is requiered" on a many to many field when blank=True

I have an object I would like to be able to make via django-rest-api UI.
This object has a manytomany field that holds other objects on it.
Even though that field is blank param is set to True, I get a response that "this field is requiered".
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.CharField(max_length=200, null=True)
description = models.CharField(max_length=200, null=True)
content = HTMLField('Content', null=True)
black_listed = models.ManyToManyField('profile_app.Profile', related_name='black_listed_posts', blank=True)
score = models.PositiveIntegerField(null=True, default=0, validators=[MaxValueValidator(100)])
serializers.py:
class PostSerializer(serializers.HyperlinkedModelSerializer):
black_listed = ProfileSerializer(many=True)
read_only = ('id',)
def create(self, validated_data):
self.black_listed = []
class Meta:
model = Post
fields = ('id', 'title', 'slug', 'description',
'content',
'black_listed', 'score')
views.py:
class PostViewSet(ModelViewSet):
serializer_class = PostSerializer
queryset = Post.objects.all()
lookup_field = "slug"
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.black_listed = []
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)
As you can see, i tried overriding the create() method on both serializer and viewset, but that didnt work and still gave me that the black_list field is requiered.
What i expected that if the field is not required in the db, then the serializer can set it to None on the creation
what am i missing here?
EDIT:
ProfileSerializer:
class ProfileSerializer(serializers.ModelSerializer):
interests = InterestSerializer(read_only=True, many=True)
class Meta:
model = Profile
fields = ('slug', 'user_id', 'image', 'role', 'work_at', 'interests')
You should provide the required=False argument in your serializer declaration:
class PostSerializer(...):
black_listed = ProfileSerializer(many=True, required=False)
# __________________________________________^
If you want to be able to post null values for this field, you may also add allow_null=True.