I am trying to get GeoJSON response using django rest framework but facing issue
argument of type 'NoneType' is not iterable
This is my code
class NewPark(models.Model):
name = models.CharField(max_length=256)
geometry = models.GeometryField(srid=3857, null=True, blank=True)
objects = models.GeoManager()
class Meta:
db_table = u'new_park'
def __unicode__(self):
return '%s' % self.name
class NewParkSerializer(GeoFeatureModelSerializer):
class Meta:
model = NewPark
geo_field = "geometry"
fields = ('id', 'name', 'geometry')
class NewParkViewSet(viewsets.ModelViewSet):
def get_queryset(self):
queryset = NewPark.objects.all()
return queryset
When i change serialize type to 'erializers.GeoModelSerializer' then it is working, but i want GEOJSON response
I have searched about GeoFeatureModelSerializer but cannot find any example geo_field = "geometry". All example are about geo_field = "point"
Please help me to figure out this issue?
You might only have the above error in the browsable API, because the default djanog-rest-framework html template renderer does not work with the GeoJson format.
To test if that is the case, try to call your api endpoint requesting it in json format, i.e. /api/newpark.json or equivalently /api/newpark?format=json. This should show you geojson data, if it does your backend API is fine, and the problem is the browsable html form.
If you want a browsable API for your GeoJson endpoint, then you might need to change the html template used to render your API end point.
Related
I am trying to implement drf-spectacular to an existing Django REST API.
However I am getting the following error, when attempting to run
./manage.py spectacular --file schema.yml
Error:
python3.7/site-packages/django_filters/filterset.py", line 352, in get_filters
"%s" % ', '.join(undefined)
TypeError: 'Meta.fields' contains fields that are not defined on this FilterSet: client, tenant_id, subtenant_id, data_stream_id
The filters do work, but don't seem to play nicely with the drf-spectacular lib. Can anyone please advise on how this might be solved?
Specs as follows:
Python 3.7.2
Django 3.0.2
django-filter 2.2.0
django-rest-framework 0.1.0
djangorestframework 3.12.1
drf-spectacular 0.12.0
Viewset example:
class subModelViewSet(viewsets.ModelViewSet):
"""Standard ViewSet for the DataStream Model."""
queryset = DataStream.objects.all()
serializer_class = DataStreamSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ('client', 'tenant_id', 'subtenant_id', 'data_stream_id',)
Serializer example:
class DataStreamSerializer(serializers.ModelSerializer):
"""Class to validate an uploaded DataStream."""
class Meta:
"""Nested Meta Class."""
model = DataStream
fields = '__all__'
Turns out it was a Viewset that was trying to apply filterset_fields on a subModel that did not directly contain those fields, but rather referenced by ForeignKey on another Parent Model. While I thought these filters were working as expected, I think the actual filtering on those fields was being performed by middleware in this case.
Removing the fields from the Viewset allows the schema to be generated with no issue. I am able to access the /docs page fully rendered and /schema endpoint working. Very impressed with drf-spectacular thus far.
example model:
class DataSteam(models.Model):
"""Class for DataSteam Model."""
client = models.CharField(max_length=200)
tenant_id = models.CharField(max_length=200)
subtenant_id = models.CharField(max_length=200)
data_stream_id = models.CharField(max_length=200)
class subModel(models.Parent):
"""Class for storing linked datastream records."""
ds = models.ForeignKey(DataStream, on_delete=models.CASCADE)
I wanna change all fields of a json object except 'pk' in DRF. I just need to keep one json data. When adding a new data ,this one should override existing data. Is there a way to do it with django ?
my models.py
class ClientUser2(models.Model):
phone_number = models.CharField(max_length=20,unique=True)
name = models.CharField(max_length=100,blank=True)
status = models.IntegerField(default=1)
class ClientNameSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ClientUser2
fields = ('url','phone_number','name','status','pk')
my views.py
class ClientViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows messages to be viewed or edited.
"""
queryset = ClientUser2.objects.all()
serializer_class = ClientNameSerializer
and it's my api root
api_root
If you want to be able to only retrieve and update models you can use RetrieveUpdateApiView
Reference : https://www.django-rest-framework.org/api-guide/generic-views/#retrieveupdateapiview
I'm using djangorestframework==3.0.3
So I would like to display an upload field that doesn't exist on the model. I don't want to save the uploaded file to the model. I want to handle it separately. I get this error with the code below 'XYZModelName' object has no attribute 'upload_file'.
class XYZSerializer(MyParentSerializer):
upload_file = serializers.FileField(use_url=False)
class Meta:
model = models.XYZModelName
fields = ('name', 'active', 'upload_file')
class XYZModelName(TrackedModel):
name = models.CharField(max_length=50)
active = models.BooleanField(default=False)
The error makes since as I don't have upload_file as an attribute on the model. I don't want upload_file on the model.
How do I show the field in the browseable api?
I've also tried creating custom fields overriding the to_internal_value and to_representation methods.
You'll need to define a function on the model that returns the URL (it needs to be somehow related to your object obviously)
def get_file_url(self):
return settings.STORAGE_LOCATION + 'some/path/' + str(self.pk) + '.png'
And then you can use that in your serializer, like:
upload_file = serializers.FileField(source='get_file_url')
To save the file during POST, you'll need to override the create method of your serializer, like:
def create(self, validated_data):
file = validated_data['upload_file']
# save file code here
del validated_data['upload_file']
return XYZ.objects.create(**validated_data)
I want to create entries in two tables (Log and Post) using the DRF Browseable API POST form.
The example below is contrived, but it outlines what I am trying to do.
class Post(models.Model):
info = models.CharField()
class Log(TimeStampedModel):
ip = models.GenericIPAddressField(('IP Address'))
name = models.CharField()
data = models.ForeignKey(Post)
I want to use the browseable API to submit a form to create a Log entry. Here are the serializers:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('info',)
class LogSerializer(serializers.ModelSerializer):
data = serializers.Field()
class Meta:
model = Log
fields = ('ip', 'name', 'data')
The problem with the above is that serializer.Field is read only so does not show up on the POST form. If I change it to CharField it shows up, but then I get an error because an instance of a Post is expected not just a field of the Post object.
Here are my views:
class LogMixin(object):
queryset = Log.objects.all()
serializer_class = LogSerializer
class LogList(LogMixin, ListCreateAPIView):
pass
class LogDetail(LogMixin, RetrieveUpdateDestroyAPIView):
pass
What's the correct way of doing this?
From what I can tell you want to create a nested Log object. There are 2 ways of doing this:
Send 2 POST Requests, One to create the Post, and the other to create the Log contained the received HTTP 200 data from the API.
(Django and best way) Send the data all in one POST and parse it server side. Django Rest Framework takes care of this for you.
I have changed your code so that it should work.
Source
class LogSerializer(serializers.ModelSerializer):
class Meta:
model = Log
fields = ('ip', 'name')
class PostSerializer(serializers.ModelSerializer):
log = LogSerializer()
class Meta:
model = Post
fields = ('info', 'log')
views.py
import generics
class PostCreateAPIView(generics.CreateAPIView):
model = Post
serializer_class = PostSerializer
Then you can send a POST Request containing 'info', 'ip', and 'name'.
This is a hacky way and the best way to use the nested serializer as stated above. But just to show another way I am posting it here.
# Add New Item
#api_view(('POST',))
def add_new_item(request):
request.data['areaname_name'] = request.data['item_areaname']
request.data['buildingname_name'] = request.data['item_buildingname']
item_serializer = TblItemSerializer(data=request.data)
area_name_serializer = TblAreanameSerializer(data=request.data)
building_name_serializer = TblBuildingnameSerializer(data=request.data)
response = []
if item_serializer.is_valid() & area_name_serializer.is_valid() & building_name_serializer.is_valid():
# Save To Item Table
item_serializer.save()
# Save Unique Values Into Areaname and Buildingname Tables
area_name_serializer.save()
building_name_serializer.save()
return Response(item_serializer.data, status=status.HTTP_201_CREATED)
else:
response.append(item_serializer.errors)
response.append(area_name_serializer.errors)
response.append(building_name_serializer.errors)
return Response(response, status=status.HTTP_400_BAD_REQUEST)
In the error response you could also use (depending on how you want to handle on client side)
merge = {**item_serializer.errors, **area_name_serializer.errors, **building_name_serializer.errors}
return Response(merge, status=status.HTTP_400_BAD_REQUEST)
I have an optional ImageField in my model with a default image. I am using Django Rest Framework for the model api. However when I try to post (post request outside the browser) without images I continue getting the error:No file was submitted. Check the encoding type on the form.
models.py
class Detector(models.Model):
average_image = models.ImageField(upload_to='average_image/',
default='average_image/default.png',
null=True, blank=True)
serializer.py
class DetectorSerializer(serializers.ModelSerializer):
class Meta:
model = Detector
fields = ('average_image')
views.py
class DetectorAPIList(generics.ListCreateAPIView):
serializer_class = DetectorSerializer
What am I missing?
Thanks for your time!
i hit the same issue, i had to explicitly mark my image field in the serializer as blank:
class DetectorSerializer(serializers.ModelSerializer):
average_image = serializers.ImageField(source='average_image', blank=True)
class Meta:
model = Detector
fields = ('average_image',)
It seems that rest framework is unable to grab it from model or something. After this i am able to POST data to the API using requests like this:
import requests
from requests.auth import HTTPBasicAuth
r = requests.post(
'http://localhost:8000/detectors/',
data={'some': 'data'},
auth=HTTPBasicAuth('user', 'password')
)
print r.text
And this is the response, as you can see it used the default from the model field definition:
{"average_image": "average_image/default.png"}
You can also try to POST with specified file:
r = requests.post(
'http://localhost:8000/detectors/',
files={'average_image': open('/path/to/image.jpg')},
data={'some': 'data'},
auth=HTTPBasicAuth('user', 'password')
)
print r.text
Hope this helps.