I am trying to make a simple api for marine vessels and ships and I am using django-rest-framework as the library to generate the api. With models that has simple fields like char fields and integers in the models level everything is working properly (create, delete, update, list, get).
The problem is with image fields whenever I try to post a record that has images it does not detect it. And it always causes an error "sequence item 1: expected string, NoneType found"
Below is my model, serializer, and view files.
serializer
class VesselSerializer(serializers.ModelSerializer):
image = serializers.ImageField(source='image')
class Meta:
model = Vessel
fields = ('image', 'id', 'vesselType', 'name')
class VesselTypeSerilizer(serializers.ModelSerializer):
class Meta:
model = VesselType
models
def vessel_file_name(instance, filename):
return '/'.join(['vessels', instance.id, filename]) #the error is in this line
class VesselType(models.Model):
name = models.CharField(max_length=50)
class Vessel(models.Model):
name = models.CharField(max_length=50)
vesselType = models.ForeignKey(VesselType)
image = models.ImageField(upload_to=vessel_file_name, null=True)
def __unicode__(self):
return u'%s' % self.name
views
class VesselList(generics.ListCreateAPIView):
queryset = Vessel.objects.all()
serializer_class = VesselSerializer
fields = ('url', 'id', 'image', 'vesselType')
def post(self, request, format=None):
print 'entered here'
print '%' * 10
print request.DATA
print request.FILES
print '%' * 10
serializer = VesselSerializer(data=request.DATA, files=request.FILES)
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)
class VesselDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Vessel.objects.all()
serializer_class = VesselSerializer
It should be noted as well that the request.FILES and request.DATA when printed are displayed correctly yet that error appears which indicates that there is no file name although the name appears in request.FILES.
I am stuck in this problem for several hours and I can't seem to find what the problem is or what I am doing wrong. Any help would be appreciated.
The problem is that when vessel_file_name is called, the instance object is not saved to the db and instance.id is None.
Related
After some hours of research, I still can't find the answer to the issue I am facing.
I am trying to add some simple validation to a field in one of my models.
In serializers.py:
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = '__all__'
def validate_name(self, value):
"""
Check that the brand name has at least 20 characters
"""
if len(value) < 20:
raise serializers.ValidationError(
"The name of the brand should be longer than 20 characters")
return value
I am using function based views:
#api_view(['POST'])
def brandCreate(request):
data = request.data
try:
brand = Brand.objects.create(
name=data['name'],
)
serializer = BrandSerializer(brand, many=False)
return Response(serializer.data)
except:
message = {'detail': 'Brand is not created'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
And this is my model:
class Brand(models.Model):
name = models.CharField(max_length=80)
logo = models.ImageField(null=True, blank=True)
def __str__(self):
return str(self.name)
After sending a POST request with postman, the record is successfully created, even though the name is shorter than 20 characters!
Any hints on what I am missing here? Thank you!
You're not using it correctly. You need to call is_valid method.
#api_view(['POST'])
def brandCreate(request):
data = request.data
serializer = BrandSerializer(data=data)
if serializer.is_valid():
serialiazer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I am using the base Django Auth User for my user handling and have authentication working. Now I am trying to create a Post method for my Rest API that automatically gets the user from the request, then gets all of the data input, and saves it.
I have tried various attempts at serialization. I also had this working as just a plain Django website, but now things are getting interesting making it into an API.
Here is my model:
class UserIncome(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
preTaxIncome = models.DecimalField(max_digits=15, decimal_places=2)
savingsRate = models.DecimalField(max_digits=3, decimal_places=2)
taxRate = models.DecimalField(max_digits=3, decimal_places=2)
Here is my Serializer(Base, no attempts at making the foreign key):
class UserIncomeSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserIncome
fields = ('id', 'user', 'preTaxIncome', 'savingsRate', 'taxRate')
Here is the view(Again, just the base. No attempts at foreign key):
class UserIncomeList(APIView):
#List all snippets, or create a new snippet.
def get(self, request, format=None):
userIncome = models.UserIncome.objects.get(user=request.user)
serializer = Serializers.UserIncomeSerializer(userIncome, many=False)
return Response(serializer.data)
def post(self, request, format=None):
serializer = Serializers.UserIncomeSerializer(data=request.data)
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)
Should I just make the foreign key the user ID and get that somehow?
Thank you!
I figured it out!
I removed the user field from the serializer, then in the post method of UserIncomeList I made the save method:
serializer.save(user = request.user)
I can't save the image to the image field.
Error
"image": [
"No file was submitted."
]
models.py
class MyImages(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField(upload_to='myphoto', null=False, max_length=255, blank=False)
views.py
class ImageList(APIView):
parser_classes = (MultiPartParser, FileUploadParser,)
def post(self, request):
file_serializer = MyImageSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serialiser.py
class MyImageSerializer(serializers.ModelSerializer):
class Meta:
model = MyImages
fields = ('id', 'name', 'image')
when using postman for file upload the file name is returned in view.py instead of the file object.
I have also seen these threads but not working
1 2
FileUploadParser populates request.data with a file key containing uploaded file.
However, ImageSerializer needs to serialize image this from the request.
Ergo, you need to specify fields explicitly. e.g.
class ImageFileField(serializers.Field):
def to_representation(self, obj):
return obj.image.url
def to_internal_value(self, data):
return data['file']
class MyImageSerializer(serializers.ModelSerializer):
name = serializers.CharField()
image = ImageFileField(source='*')
class Meta:
model = MyImages
I couldn't find any bug in your code when I tried to reproduce the error.
Make sure that uncheck the application/json content type
Then the postman console will be as follows,
I am using the django rest framework and I have a very simple model of Posts for a particular user which I have serialised in the following manner.
Serializers.py
class PostSerializer(serializers.HyperlinkedModelSerializer):
image = serializers.ImageField(max_length=None, use_url=True)
question = serializers.CharField(required=False)
ayes = serializers.CharField(required=False)
nays = serializers.CharField(required=False)
neutrals = serializers.CharField(required=False)
class Meta:
model = Posts
fields = ('user','question', 'image','ayes', 'nays', 'neutrals')
My models.py is as follows
class Posts(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
question = models.TextField(max_length=500, blank=True)
image = models.ImageField('optionalImage', upload_to='images/posts/', default='/images/posts/blank.png')
ayes = models.TextField(max_length=200, default=0)
nays = models.TextField(max_length=200, default=0)
neutrals = models.TextField(max_length=200, default=0)
When I tried posting to this I kept getting NOT NULL Integrity constraint error of user_id. Hence I added context={'request': request}) to the serializer which ends up giving me the following error:
Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
My views.py is as follows:
views.py
#permission_classes((IsAuthenticated, ))
class PostsView(generics.ListCreateAPIView):
queryset = Posts.objects.all()
serializer_class = PostSerializer
def get(self, request, format=None):
snippets = Posts.objects.filter(pk=request.user.id)
serializer = PostSerializer(snippets, many=True,context={'request': request})
return Response(serializer.data)
def post(self, request, format=None):
posts = PostSerializer(data=request.data,context={'request': request})
if posts.is_valid():
posts.save()
return Response("YOLO", status=status.HTTP_201_CREATED)
else:
return Response(posts.errors, status=status.HTTP_400_BAD_REQUEST)
All other fields of mine have posted correctly when I set default=0 in my model for user. I am unable to submit the foreign key user which needs to be saved on every post. What am I doing wrong here? Am I following the correct method?
Since you don't want to send your user, you should remove it from the serializer's field.
Next, you want to set the post's user to the current user. To achieve that, you want to pass the request.user to the serializer's data by changing the save method to:
posts.save(user=request.user)
It's explained in the documentation and in the tutorial
I am using DRF to expose some API endpoints.
# models.py
class Project(models.Model):
...
assigned_to = models.ManyToManyField(
User, default=None, blank=True, null=True
)
# serializers.py
class ProjectSerializer(serializers.ModelSerializer):
assigned_to = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(), required=False, many=True)
class Meta:
model = Project
fields = ('id', 'title', 'created_by', 'assigned_to')
# view.py
class ProjectList(generics.ListCreateAPIView):
mode = Project
serializer_class = ProjectSerializer
filter_fields = ('title',)
def post(self, request, format=None):
# get a list of user.id of assigned_to users
assigned_to = [x.get('id') for x in request.DATA.get('assigned_to')]
# create a new project serilaizer
serializer = ProjectSerializer(data={
"title": request.DATA.get('title'),
"created_by": request.user.pk,
"assigned_to": assigned_to,
})
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.data, status=status.HTTP_201_CREATED)
This all works fine, and I can POST a list of ids for the assigned to field. However, to make this function I had to use PrimaryKeyRelatedField instead of RelatedField. This means that when I do a GET then I only receive the primary keys of the user in the assigned_to field. Is there some way to maintain the current behavior for POST but return the serialized User details for the assigned_to field?
I recently solved this with a subclassed PrimaryKeyRelatedField() which uses the id for input to set the value, but returns a nested value using serializers. Now this may not be 100% what was requested here. The POST, PUT, and PATCH responses will also include the nested representation whereas the question does specify that POST behave exactly as it does with a PrimaryKeyRelatedField.
https://gist.github.com/jmichalicek/f841110a9aa6dbb6f781
class PrimaryKeyInObjectOutRelatedField(PrimaryKeyRelatedField):
"""
Django Rest Framework RelatedField which takes the primary key as input to allow setting relations,
but takes an optional `output_serializer_class` parameter, which if specified, will be used to
serialize the data in responses.
Usage:
class MyModelSerializer(serializers.ModelSerializer):
related_model = PrimaryKeyInObjectOutRelatedField(
queryset=MyOtherModel.objects.all(), output_serializer_class=MyOtherModelSerializer)
class Meta:
model = MyModel
fields = ('related_model', 'id', 'foo', 'bar')
"""
def __init__(self, **kwargs):
self._output_serializer_class = kwargs.pop('output_serializer_class', None)
super(PrimaryKeyInObjectOutRelatedField, self).__init__(**kwargs)
def use_pk_only_optimization(self):
return not bool(self._output_serializer_class)
def to_representation(self, obj):
if self._output_serializer_class:
data = self._output_serializer_class(obj).data
else:
data = super(PrimaryKeyInObjectOutRelatedField, self).to_representation(obj)
return data
You'll need to use a different serializer for POST and GET in that case.
Take a look into overriding the get_serializer_class() method on the view, and switching the serializer that's returned depending on self.request.method.