i have a simple django rest framework and i want to know how can use StreamingHttpResponse in my project.
my model is like this:
class Article(models.Model):
user = models.CharField(max_length=100)
title = models.CharField(max_length=200)
description = models.TextField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
and serializer for this model is like this:
class ArticleSerializers(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['id', 'user', 'title', 'description', 'date']
i think my problem is in mu view or url. so i wrote code in view like this:
class StreamPost(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializers
def get_queryset(self):
stream = event_stream()
response = StreamingHttpResponse(stream, status=200, content_type='text/event-stream')
response['Cache-Control'] = 'no-cache'
return response
that the event_stream is like this:
def event_stream():
initial_data = ''
while True:
list_article = list(Article.objects.all())
data = json.dumps(list_article, cls=DjangoJSONEncoder)
if not initial_data == data:
yield "\ndata: {}\n\n".format(data)
initial_data = data
time.sleep(1)
the url is like this:
router = DefaultRouter()
router.register('stream', StreamPost, basename='stream')
urlpatterns = [
path('', include(router.urls)),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
i can't track the error and so i don't know where is my problem but when run my project with this codes the problem is like this:
Object of type Article is not JSON serializable
when i change the event_stream function the error changed like this:
def event_stream():
initial_data = ''
while True:
list_article = list(Article.objects.all().values('id', 'user', 'title', 'description', 'date'))
data = json.dumps(list_article, cls=DjangoJSONEncoder)
if not initial_data == data:
yield "\ndata: {}\n\n".format(data)
initial_data = data
time.sleep(1)
the error:
Got AttributeError when attempting to get a value for field `user` on serializer `ArticleSerializers`.
The serializer field might be named incorrectly and not match any attribute or key on the `bytes` instance.
Original exception text was: 'bytes' object has no attribute 'user'.
Related
I am using serializers.Serializer instead of ModelSerializer which doesn't require Meta class but it keep saying object has no attribute Meta. Iam not sure what is the issue but when I run the localserver, the main page gives error saying api fetch error and in the terminal it says AttributeError: 'Serializer' object has no attribute 'Meta'.
My view:
class ClassView(viewsets.ModelViewSet):
queryset = Class.objects.all().order_by('-created_at')
serializer_class = ClassSerializer
serializer_action_classes = {
'get_all_students_of_a_class': ClassDetailSerializer,
}
# .annotate(total_students=Count('students_in_class'))
def get_serializer_class(self):
"""
returns a serializer class based on the action
that has been defined.
"""
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super(ClassView, self).get_serializer_class()
def get_all_students_of_a_class(self,request,pk=None):
"""
returns a class details with all the students signed up for the
class along with the subject.
"""
id = pk
if self.is_teacher(id):
online_classes = get_object_or_404(Class, id=id)
students = Student.objects.filter(online_class__id=id)
subject_details = Subject.objects.get(online_class__id=id)
total_students_in_class = online_classes.total_students_in_class
created_at = online_classes.created_at
updated_at = online_classes.updated_at
data = {
"teacher": self.get_teacher_instance(),
'total_students_in_class': total_students_in_class,
"students_in_class": students,
"subject": subject_details,
'created_at': created_at,
'last_updated_at': updated_at,
}
serializer_class = self.get_serializer_class()
serializer = serializer_class(data)
return Response(serializer.data, status=status.HTTP_200_OK)
My serializer:
class ClassDetailSerializer(serializers.Serializer):
teacher = serializers.StringRelatedField()
subject = SubjectLevelSerializer()
total_students_in_class = serializers.ReadOnlyField()
students_in_class = serializers.StringRelatedField(many=True)
created_at = serializers.DateTimeField(read_only=True)
last_updated_at = serializers.DateTimeField(read_only=True)
My url:
path("class/<int:pk>/",teacher.ClassView.as_view({"get": "get_all_students_of_a_class","delete":"destroy"}),
),
However it works and I can perform action if I go to localhost/admin and other api calls from localhost.
Add a Meta class with model = YourModel
class ExampleDetailSerializer(serializers.Serializer):
employee = serializers.StringRelatedField()
person = PersonSerializer()
class Meta:
model = Example # model name
fields = ('__all__')
Try to use "ViewSet" instead of "ModelViewSet". And while using ViewSet make sure to define list, create, etc. function by your own.
I need to retrieve a filtered set of data by calling an HTTP request using Django rest framework.
here are my API codes:
urls.py
urlpatterns = [
path('api/get_products/', views.get_products),
]
Views.py
#api_view(["GET", ])
def get_products(request):
category_name = request.data['category_name']
category_obj = Category.objects.get(name=category_name)
products_list = Product.objects.filter(category=category_obj)
serializer = ProductSerializers(products_list)
return Response(serializer.data)
and finally the serialierz.py
class CategorySerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Category
fields = ['name', 'id']
class ProductSerializers(serializers.HyperlinkedModelSerializer):
category = CategorySerializers()
class Meta:
model = Product
fields = '__all__'
and am trying to call it using a get request with the argument: {'category_name':'the_name_of_the_category' }
and it returns this error:
KeyError at /categories/api/api/get_products/
'category_name'
Your API method is a GET method. You cannot accept body with get method. You can change your API method with POST method or better one, you can get 'category_name' with url. You can add url variable like that:
path('api/get_products/<slug:category_name>', views.get_products),
and your view method:
def get_products(request,category_name):
category_obj = Category.objects.get(name=category_name)
products_list = Product.objects.filter(category=category_obj)
serializer = ProductSerializers(products_list)
return Response(serializer.data)
I'm currently setting up a GET request from my endpoint to export a PDF file based on the filters(django_filters) on my ModelViewSet and the PDF's design will based on the template_path provided. And I'm fairly new on using DRF, so I'm new to some things.
I've tried using easy_pdf for my ModelViewSet, but it doesn't show on the endpoint's GET request. And also no error was provided in the logs, I assume I'm doing something really wrong on this.
This is my Filter
class CarrierFilter(django_filters.FilterSet):
date_app_rec__gte = DateFilter(field_name='date_app_rec', lookup_expr='gte')
date_app_rec__lte = DateFilter(field_name='date_app_rec', lookup_expr='lte')
date_sample_rec__gte = DateFilter(field_name='date_sample_rec', lookup_expr='gte')
date_sample_rec__lte = DateFilter(field_name='date_sample_rec', lookup_expr='lte')
date_of_qca__gte = DateFilter(field_name='date_of_qca', lookup_expr='gte')
date_of_qca__lte = DateFilter(field_name='date_of_qca', lookup_expr='lte')
date_created__gte = DateFilter(field_name='date_created', lookup_expr='gte')
date_created__lte = DateFilter(field_name='date_created', lookup_expr='lte')
patient_name = CharFilter(field_name='patient_name', lookup_expr='icontains')
class Meta:
model = Carrier
fields = ('date_app_rec__gte', 'date_app_rec__lte',
'date_sample_rec__gte', 'date_sample_rec__lte',
'date_of_qca__gte', 'date_of_qca__lte',
'date_created__gte', 'date_created__lte',
'patient_name',)
And this is my ModelViewSet
class CarrierViewSet(XLSXFileMixin, PDFTemplateResponseMixin, ModelViewSet):
serializer_class = CarrierSerializer
permission_classes = (IsAuthenticated,)
parser_classes = (MultiPartParser,)
filename = 'carrier-reports.xlsx'
pdf_filename = 'carrier-report.pdf'
filter_class = (CarrierFilter)
filterset_fields = ('patient_name', 'promo_code')
search_fields = ('patient_name', 'promo_code', 'insurance_verified_tsg_verification')
def get_queryset(self):
user = self.request.user
position = self.request.user.position
if position == 'Manager':
queryset = Carrier.objects.filter(manager__name=user)
else:
queryset = Carrier.objects.filter(agent__name=user)
return queryset
def get(self, request, format=None):
carrier = Carrier.objects.all()
template_path = 'carrier/carrier_print.html'
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="carrier-report.pdf"'
html = render_to_string(template_path, {'carrier': carrier})
print(html)
pisaStatus = pisa.CreatePDF(html, dest=response)
return response
What I want to do is that the pdf should be in the GET request of that endpoint, same scenario as the drf_renderer_xlsx when using the xlsx from the GET. Or is there another way around on doing this?
I have to display (as in topic title) only specific data from the list which is storage on server (PYTHON/DJANGO/REST_FRAMEWORK).
F.e. I want to choose data which has planted on the server with 'id=1', but I always get back all items. Can you explain how to correct this?
'urls' file (project level) :
# URL routes - known as endpoints API
urlpatterns = [
path('admin/', admin.site.urls),
path('devices/', include('efota.api.urls')),
]
'urls' file (subordinate folder) :
urlpatterns = [
url('', views.DeviceList.as_view()),
url('<int:pk>/', views.DeviceDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
'views' file :
class DeviceList(generics.ListAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
class DeviceDetail(generics.RetrieveAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
'serializers' file:
class DeviceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
# the 'fields' controls which database attributes are available
class Meta:
model = Device
fields = (
'id',
'user',
'id_token',
'current_firmware',
'carrier_code',
'model_name',
'owner',
)
read_only_fields = ['id']
def get_url(self, obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
'models' file:
class Device(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey('auth.User', related_name='owner',
on_delete=models.CASCADE)
user = models.CharField(max_length=50, verbose_name='user')
id_token = models.CharField(max_length=1000,
verbose_name='id_token')
current_firmware = models.CharField(max_length=41,
verbose_name='current_firmware')
carrier_code = models.CharField(max_length=5,
verbose_name='carrier_code')
model_name = models.CharField(max_length=10,
verbose_name='model_name')
class Meta:
ordering = ('created', )
def __str__(self):
return self.model_name
'admin' file:
from django.contrib import admin
from .models import Device
# Register your models here.
admin.site.register(Device)
You are confusing old-style URL and new-style path syntax in your urls.py. The url() function takes a regex, and r'' matches every URL, while <int:pk>/ would only match that literal string. Use path instead.
urlpatterns = [
path('', views.DeviceList.as_view()),
path('<int:pk>/', views.DeviceDetail.as_view()),
]
To filter result returned by ListAPIView you can modify its get_queryset method as follows.
class DeviceDetail(generics.ListAPIView):
serializer_class = DeviceSerializer
def get_queryset(self):
return Device.objects.filter(id=self.kwargs['pk'])
I want to create a many-to-many relationship where one person can be in many clubs and one club can have many persons. I added the models.py and serializers.py for the following logic but when I try to serialize it in the command prompt, I get the following error - What am I doing wrong here? I don't even have a HyperlinkedIdentityField
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 503, in data
ret = super(Serializer, self).data
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 239, in data
self._data = self.to_representation(self.instance)
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 472, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File "C:\Users\user\corr\lib\site-packages\rest_framework\relations.py", line 320, in to_representation"the serializer." % self.__class__.__name__
AssertionError: `HyperlinkedIdentityField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer.
models.py
class Club(models.Model):
club_name = models.CharField(default='',blank=False,max_length=100)
class Person(models.Model):
person_name = models.CharField(default='',blank=False,max_length=200)
clubs = models.ManyToManyField(Club)
serializers.py
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = ('url','id','club_name','person')
class PersonSerializer(serializers.ModelSerializer):
clubs = ClubSerializer()
class Meta:
model = Person
fields = ('url','id','person_name','clubs')
views.py
class ClubDetail(generics.ListCreateAPIView):
serializer_class = ClubSerializer
def get_queryset(self):
club = Clubs.objects.get(pk=self.kwargs.get('pk',None))
persons = Person.objects.filter(club=club)
return persons
class ClubList(generics.ListCreateAPIView):
queryset = Club.objects.all()
serializer_class = ClubSerializer
class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = PersonSerializer
def get_object(self):
person_id = self.kwargs.get('pk',None)
return Person.objects.get(pk=person_id)
Inspecting the created serializer gives me this -
PersonSerializer(<Person: fd>):
url = HyperlinkedIdentityField(view_name='person-detail')
id = IntegerField(label='ID', read_only=True)
person_name = CharField(max_length=200, required=False)
clubs = ClubSerializer():
url = HyperlinkedIdentityField(view_name='club-detail')
id = IntegerField(label='ID', read_only=True)
club_name = CharField(max_length=100, required=False)
but serializer.data gives me the error
Edit
I realized the error could be because of url patterns, so I added the following url patterns but I still get the error -
urlpatterns = format_suffix_patterns([
url(r'^$', views.api_root),
url(r'^clubs/$',
views.ClubList.as_view(),
name='club-list'),
url(r'^clubs/(?P<pk>[0-9]+)/persons/$',
views.ClubDetail.as_view(),
name='club-detail'),
url(r'^person/(?P<pk>[0-9]+)/$',
views.PersonDetail.as_view(),
name='person-detail'),
])
You're getting this error as the HyperlinkedIdentityField expects to receive request in context of the serializer so it can build absolute URLs. As you are initializing your serializer on the command line, you don't have access to request and so receive an error.
If you need to check your serializer on the command line, you'd need to do something like this:
from rest_framework.request import Request
from rest_framework.test import APIRequestFactory
from .models import Person
from .serializers import PersonSerializer
factory = APIRequestFactory()
request = factory.get('/')
serializer_context = {
'request': Request(request),
}
p = Person.objects.first()
s = PersonSerializer(instance=p, context=serializer_context)
print s.data
Your url field would look something like http://testserver/person/1/.
I have two solutions...
urls.py
1)
If you are using a router.register, you can add the base_name:
router.register(r'users', views.UserViewSet, base_name='users')
urlpatterns = [
url(r'', include(router.urls)),
]
2)
If you have something like this:
urlpatterns = [
url(r'^user/$', views.UserRequestViewSet.as_view()),
]
You have to pass the context to the serializer:
views.py
class UserRequestViewSet(APIView):
def get(self, request, pk=None, format=None):
user = ...
serializer_context = {
'request': request,
}
serializer = api_serializers.UserSerializer(user, context=serializer_context)
return Response(serializer.data)
Like this you can continue to use the url on your serializer:
serializers.py
...
url = serializers.HyperlinkedIdentityField(view_name="user")
...
I came across the same problem. My approach is to remove 'url' from Meta.fields in serializer.py.
Following Slipstream's answer, I edited my views.py introducing the context and now it works.
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().select_related('profile').order_by('-date_joined')
serializer_class = UserSerializer
#list_route(methods=['get'], url_path='username/(?P<username>\w+)')
def getByUsername(self, request, username):
serializer_context = {
'request': request,
}
user = get_object_or_404(User, username=username)
return Response(UserSerializer(user, context=serializer_context).data, status=status.HTTP_200_OK)
You can simply pass None to 'request' key in context in situations where you just need the relative URL, e.g; testing a serializer in command line.
serializer = YourModelSerializer(modelInstance_or_obj, context={'request': None})
Following MDT's response, I use django-rest-framework, and solve it by changing request to request._request.
serializer_context = {'request': Request(request._request)}
You may simply solve it by changing the instantiation (in views.py) to thing like this:
your_serializer = YourModelSerializer(YourQuerySet_or_object, many=True,context={'request':request})
For externals urls you can simply put request at None:
context={
'request': None
},
In my case I had to change a field's name from url to any other thing. Hate automagic