Displaying Foreign Key Options in django restframework view - python

I'm having trouble displaying all the jobs as options in the apply url view see image below.
I am getting the error which says
Lists are not currently supported in HTML input
The main function I am looking for is for a list of jobs that were posted to be available for selection when applying for the job.
models.py
class Job(models.Model):
"""A Job used to create a job posting"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
description = models.TextField()
job_type = models.CharField(max_length=12, choices=JOB_TYPE_CHOICES, default='Full-Time')
city = models.CharField(max_length=255)
def __str__(self):
return self.description[:50]
class Applicant(models.Model):
"""A applicant that can apply to a job"""
job = models.ForeignKey(Job, related_name='applicants', on_delete=models.CASCADE)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(max_length=254)
phone_number = PhoneNumberField()
resume = models.FileField(upload_to=resume_file_path, validators=[validate_file_extension])
def __str__(self):
return self.first_name
I've removed some of the attributes in Job so that the code is not so long.
serializers.py
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from core.models import Job, Applicant
class JobSerializer(serializers.ModelSerializer):
"""Serializer for tag objects"""
applicants = serializers.StringRelatedField(many=True)
class Meta:
model = Job
fields = ('id', 'description', 'job_type', 'city', 'state', 'salary', 'position', 'employer', 'created_date', 'is_active', 'applicants')
read_only_fields = ('id',)
def create(self, validated_data):
"""Create a job posting with user and return it"""
return Job.objects.create(**validated_data)
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
jobs = JobSerializer(many=True, queryset=Job.objects.all())
class Meta:
model = Applicant
fields = ('id','jobs', 'first_name', 'last_name', 'email', 'phone_number', 'resume')
read_only_fields = ('id',)
views.py
class ApplyJobView(generics.CreateAPIView):
"""Allows applicants to apply for jobs"""
serializer_class = serializers.ApplyJobSerializer
I've tried adding a queryset=Job.objects.all() as an argument to the JobSerializer() in the ApplyJobSerializer class in my serializers.py field. However I get an error that says
TypeError: __init__() got an unexpected keyword argument 'queryset'

You can select an existing job in the form.
class JobSerializer(serializers.PrimaryKeyRelatedField, serializers.ModelSerializer):
"""Serializer for tag objects"""
applicants = serializers.StringRelatedField(many=True)
class Meta:
model = Job
fields = ('__all__')
read_only_fields = ('id',)
def create(self, validated_data):
"""Create a job posting with user and return it"""
return Job.objects.create(**validated_data)
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
jobs = JobSerializer(many=True, queryset=Job.objects.all())
class Meta:
model = Applicant
fields = ('__all__')
read_only_fields = ('id',)
This result as I have created a new job with description is desc

Related

How do I do a post request with related serializers Django rest framework

I have model Package with Supplier and PackageSize, foreign keys and a field to which is also a foreign key of a Shipping model which contains where the supplier wants to ship the package so to make sure I a user can submit the whole information in one request, I created my serializers and linked them like such.
serializers.py
from users.models import Supplier
from packages.models import Package , PackageSize , ShippingLocation , Shipping
class ShippingLocationSerializer(serializers.ModelSerializer):
class Meta:
model= ShippingLocation
fields = ['latitude','longitude']
def create(self, validated_data):
return ShippingLocation(**validated_data)
class PackageSizeSerializer(serializers.ModelSerializer):
class Meta:
model= PackageSize
fields = ['length', 'width' ,'height' ,'weight']
def create(self, validated_data):
"""
docstring
"""
pass
class ShippingSerializer(serializers.ModelSerializer):
location = ShippingLocationSerializer(source='location_set')
class Meta:
model = Shipping
fields = [
'first_name',
'last_name',
'phone_number',
'email',
'street_address',
'village',
'district',
'country',
'location'
]
def create(self, validated_data):
"""
docstring
"""
pass
class SupplierPackageSerializer(serializers.ModelSerializer):
size = PackageSizeSerializer(source='size_set')
shipping_location= ShippingSerializer(source='to_set')
class Meta:
model = Package
fields = ['supplier', 'name', 'size', 'shipping_location', ]
read_only_fields = ['supplier']
def create(self, validated_data):
user =Supplier.objects.get(username=self.context['request'].user)
return Package(supplier=user, **validated_data )
and created my views like such
view.py
from rest_framework import generics
from packages.models import Package
from .serializers import , SupplierPackageSerializer
from rest_framework.permissions import IsAuthenticated
class SupplierPackageViewSet(generics.ListCreateAPIView):
serializer_class = SupplierPackageSerializer
queryset = Package.objects.all().order_by('-name')
permission_classes = [IsAuthenticated]
models.py
from django.db import models
from django.conf import settings
from users.models import Supplier
class ShippingLocation(models.Model):
latitude = models.IntegerField()
longitude = models.IntegerField()
class Shipping (models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
phone_number = models.CharField(max_length=14)
email = models.EmailField()
street_address = models.CharField(max_length=30)
village = models.CharField(max_length=30)
district = models.CharField(max_length=30)
country = models.CharField(max_length=30)
location = models.OneToOneField(ShippingLocation , default=None, on_delete=models.CASCADE )
# Returns the string representation of the model.
def __str__(self):
return self.email
class PackageSize(models.Model):
length = models.IntegerField()
width = models.IntegerField()
height = models.IntegerField()
weight = models.IntegerField()
# Create your models here.
class Package(models.Model):
TYPE = (
('1', 'Envelope'),
('2', 'Parcel'),
('2', 'Soft'),
('2', 'Freezed'),
)
TYPE = (
('1', 'CREATED IN SYSTEM'),
('2', ''),
('2', 'Soft'),
('2', 'Freezed'),
)
name = models.CharField(max_length=30)
supplier = models.ForeignKey( Supplier , on_delete=models.DO_NOTHING)
to = models.ForeignKey(Shipping, default=None, on_delete=models.CASCADE )
size = models.ForeignKey( PackageSize, default=None, on_delete=models.CASCADE )
type = models.CharField(max_length=1, default="1", choices=TYPE)
def __str__(self):
return self.name
the challenge is when I submit the data it is validated very well but it cannot be saved and I get this error
TypeError at /supplier/package
Package() got an unexpected keyword argument 'size_set'
Model Package has foreign key to PackageSize ( only one size per package) so source is not size_set but just size
size = PackageSizeSerializer()
EDIT:
You will also have to override create method on serializer to save related object as documented in writable nested serializer
Something in a line of
def create(self, validated_data):
size_data= validated_data.pop('size', None)
if size_data:
package_size= PackageSize.objects.get_or_create(**size_data)[0]
validated_data['size'] = package_size
return Package.objects.create(**validated_data)
EDIT!
serializers.py
class StringSerializer(serializers.StringRelatedField):
def to_internal_value(self, value):
return value
class SupplierPackageSerializer(serializers.ModelSerializer):
item1 = StringSerializer()
item2 = StringSerializer()
size = serializers.SerializerMethodField()
shipping_location= serializers.SerializerMethodField()
class Meta:
model = Package
fields = ['supplier', 'name', 'size', 'shipping_location', ]
read_only_fields = ['supplier']
# add this
def get_size(self, obj):
return PackageSizeSerializer(obj.item1).data
def get_shipping_location(self,obj):
return ShippingSerializer(obj.item2).data
DOCS:
https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield
So basically The StringSerializer() class is a way to return a string representation of the model data.
While SerializerMethodField This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.

how to make a comment post api in django related to the single blogpost detail?

I have a page where a blog post detail is displayed. Under the post, there is a section where user can comment after inputing thier name subject and text in a comment box. Now i have to make an api for this. I want to make a post api such that that comment is stored/associated to that particular blogpost detail. That means i need blogpost id to pass while posting comment. How to do that??
class BlogPost(models.Model):
CATEGORY_CHOICES = (
('travel_news', 'Travel News',),
('travel_tips', 'Travel Tips',),
('things_to_do', 'Things to Do',),
('places_to_go', 'Places to Go'),
)
image = models.ImageField(blank=True, null=True)
categories = models.CharField(max_length=64, choices=CATEGORY_CHOICES, default='travel_news')
description = models.CharField(max_length=255)
content = RichTextUploadingField()
# todo support for tags
tags = models.CharField(max_length=255, default='#travel') #todo
date_created = models.DateField()
#property
def html_stripped(self):
from django.utils.html import strip_tags
return strip_tags(self.content)
#property
def comments(self):
return self.comments_set.all()
Here are my serializers:
class CommentPostSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
# fields = '__all__'
fields = ['name', 'email', 'subject', 'comment',]
class BlogPostSerializer(serializers.ModelSerializer):
comments = CommentListSerializer(many=True)
class Meta:
model = BlogPost
fields = ['image', 'categories', 'description', 'content', 'tags', 'date_created', 'comments']
# fields = '__all__'
Here is my view:
class CommentCreateAPIView(CreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentPostSerializer

Python Django saved posts computed method

Assuming im using the default django model, a Post model (code below) and a SavedPost model that links a User to a Post (if the certain user with the certain post exists then that post is saved for that user) and a Follower model that links 2 user (similar to SavedPost).
What im trying to do: An API that for a user, they get all posts for the users they follow, in addition each of these posts has an extra 'field' to say if that post is saved or not.
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post_type = models.CharField(max_length=1, choices=[('B', 'Blog'), ('V', 'Video')], default='B')
file_path = models.URLField(null=True)
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class SavedPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# A user can save a post only once.
unique_together = ('user', 'post')
class Follower(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user")
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name="follower")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# A user can follow another user only once
unique_together = ('user', 'follower')
Post serilializer:
class PostSerializer(serializers.ModelSerializer):
"""
Nested serializer for post using SimpleUser and Kingdom.
"""
class Meta:
model = Post
fields = ('id', 'user', 'post_type', 'file_path',
'title', 'description', 'created_at', 'updated_at')
def to_representation(self, instance):
data = super().to_representation(instance)
data['user'] = UserSerializer(
User.objects.get(pk=data['user'])).data
return data
API View:
#permission_classes([IsAuthenticated,])
#api_view(['GET'])
def get_following(request):
user = request.user
following = Follower.objects.filter(follower=user).values('user')
# saved_posts = SavedPost.objects.filter(user=user, post__user__in=following).order_by('-post__created_at')
posts = Post.objects.filter(user__in=following).order_by('-created_at')
serializer = PostSerializer(posts, many=True, context={'request': request})
return JsonResponse(serializer.data, safe=False)
So far with the view I made I can get all the posts that the request.user follows but it doesnt say if they are saved or not. I am looking for say 'is_saved' boolean on post to say if that post is saved for that user or not.
Any help/method to do this appreciated. Thank you.
Use serializers.SerializerMethodField as
class PostSerializer(serializers.ModelSerializer):
is_saved = serializers.SerializerMethodField()
def get_is_saved(self, post_instance):
return SavedPost.objects.filter(user=post_instance.user, post=post_instance).exists()
class Meta:
model = Post
fields = ['id', 'user', 'post_type', 'file_path',
'title', 'description', 'created_at', 'updated_at', 'is_saved']
def to_representation(self, instance):
data = super().to_representation(instance)
data['user'] = UserSerializer(
User.objects.get(pk=data['user'])).data
return data
First of all, just to be clear, I will be defining the related_name option for the ForeignKeys in SavedPost - it's up to you to decide whether to implement this or not:
class SavedPost(models.Model):
user = models.ForeignKey(User, related_name="saved", on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name="saved", on_delete=models.CASCADE)
...
Now, in your PostSerializer, you could add this field (remember to add it to the fields variable in the Meta inner class - that is if you're using ModelSerializer):
class PostSerializer(serializers.ModelSerializer):
saved = SavedPostSerializer(many=True)
...
To finish it off, define your SavedPostSerializer - above PostSerializer, if in the same file/module:
class SavedPostSerializer(serializers.ModelSerializer):
class Meta:
model = SavedPost
fields = "__all__"
With this, your json should have a nested field with the saved key containing an array of SavedPosts, if there are any related to the Posts retrieved.

How to access reversed relationship using Django rest framework

Here are my models :
class Profile(models.Model):
user = models.ForeignKey(User, related_name="profile", on_delete=PROTECT)
plan = models.ForeignKey(Plans, on_delete=PROTECT)
full_name = models.CharField(max_length=2000)
company_name = models.CharField(max_length=50, null=True, blank=True)
activation_token = models.UUIDField(default=uuid.uuid4)
activated = models.BooleanField(default=False)
thumb = models.ImageField(upload_to='uploads/thumb/', null=True, blank=True)
renew_data = models.DateField()
is_paid = models.BooleanField(default=False)
And as you see the Profile model have user field that is related to the Abstract user of django framework. now here is how i call them using an API :
Serializers
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Profile
fields = ['company_name']
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile_set = ProfileSerializer(
read_only=True, many=True) # many=True is required
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile_set']
But when I call the API it shows only the fields username and 'id but not the profile_set
Your UserSerializer should like this,
class UserSerializer(serializers.HyperlinkedModelSerializer):
# no need to set `profile.all` as you have related name profile defined in your model
profile_set = ProfileSerializer(source='profile', many=True)
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile_set']
OR,
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = ProfileSerializer(many=True) # as you have related name `profile`
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile']
Try setting the source of your serializer:
profile_set = ProfileSerializer(
source='profile.all',
read_only=True, many=True
)
It looks like you've set the related_name on your foreign key:
user = models.ForeignKey(User, related_name="profile", on_delete=PROTECT)
This defines the reverse relation name, so that's how you need to refer to it in DRF, too:
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = ProfileSerializer(read_only=True, many=True)
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile']
Since it's clearly a plural, I'd also suggest you rename profile to profiles.

Getting attribute error when trying to access the nested serializer in Django Rest Framework

I am getting following error while using the PostSerializer:
Got AttributeError when attempting to get a value for field
full_name on serializer UserSerializer. The serializer field might
be named incorrectly and not match any attribute or key on the long
instance. Original exception text was: 'long' object has no attribute
'full_name'.
Serializers are as follows:
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False, allow_null=True)
class Meta:
model = Post
fields = ('id', 'author', 'message', 'rating', 'create_date', 'close_date',)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'full_name',)
View:
class PostMixin(object):
model = Post
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
queryset = model.objects.all()
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
User model:
class User(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, null=True)
full_name = models.CharField(max_length=50, blank=False)
phone = models.CharField(max_length=20, unique=True, null=True)
about = models.CharField(max_length=255, blank=True)
type = models.CharField(max_length=1, default='U')
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name']
def __unicode__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.full_name
Problem
Got AttributeError when attempting to get a value for field full_name on serializer UserSerializer.
The model User in Django has no such field called full_name.
There is though a method get_full_name() that does what you want.
Solution
So try using it through a SerializerMethodField
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username') # no full_name here
full_name = serializers.SerializerMethodField('get_full_name')
This will add a field called full_name to your serialized object, with the value pulled from User.get_full_name()
Check you are using your custom model and not Django's User model
You've customized your own User model, but since that models has full_name, you shouldn't have gotten that error in the first place, so double check you are not referencing Django's default User model first.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User # <--- Make sure this is your app.models.User,
# and not Django's User model
fields = ('id', 'username', 'full_name',) # This is OK on your User model
or just
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name')

Categories

Resources