My goal very much resembles to what has been asked in this question but from the perspective of DRF, rather than forms.
So basically the question is, how can I get the newly created object in the following code snippet:
TestSerializer(serializers.ModelSerializer)
class Meta:
fields = '__all__'
model = TestModel
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def create(self, request, *args, **kwargs):
response = super(TestView, self).create(request, *args, **kwargs)
created_model_instance = .... ?
print(created_model_instance.id)
return response
You can override perform_create and use serializer.save to get the created object, like:
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def perform_create(self, serializer):
"""Some doc here!"""
obj = serializer.save()
print(obj.id)
Related
Something strange happened: I was defining an endpoint and initially two fields were visible in the API form: model_id and payload, as given in the model definition:
### models.py:
class CarModel(models.Model):
model_id = models.CharField(max_length=10, primary_key=True)
name = models.CharField(max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class Calculator(models.Model):
model = models.ForeignKey(CarModel, on_delete=models.CASCADE)
payload = models.TextField()
def model_id(self):
return self.model.model_id
def __str__(self):
return f"Calculations for {self.model.name}"
### serializers.py:
class CalculatorSerializer(serializers.ModelSerializer):
model_id = serializers.SerializerMethodField()
class Meta:
model = Calculator
fields = ['model_id', 'payload']
def get_model_id(self, obj):
return obj.model_id()
### views.py:
class CalculatorViewSet(viewsets.ModelViewSet):
serializer_class = CalculatorSerializer
queryset = Calculator.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(f"{serializer.data.upper()}", status=status.HTTP_200_OK)
So, both fields were visible, but POST requests ended in the AttributeError: 'collections.OrderedDict' object has no attribute 'model_id'. Trying to fix that, I eventually and accidentally removed model_id from view - it doesn't display in DRF's forms. And the AttributeError still persists.
What is wrong with this piece of code?
OK, it turns out that defining fields in this manner:
fields = '__all__'
makes also the model_id visible. Still, no idea why explicit insert doesn't work.
In case of the other issue, the AttributeError, I had to pull the value out of an OrderedDict. Modified method looks like this:
def get_model_id(self, obj):
return obj["model"].model_id
Beside that, I found one more error inside views.py's create method: serializer.data won't implement upper() method; some key, in my case serializer.data['payload'], has to be referenced, so for example:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
res = {
"payload": f"{serializer.data['payload'].upper()}"
}
return Response(res, status=status.HTTP_200_OK)
Edit: It appears that the problem I'm experiencing is directly related to the formset_factory() call. For some reason or other, I have been unable to access the kwargs from the view if I pass the form through the function.
I'm building a web app that utilizes high normalization in the data-structure. There are many many-to-many and one-to-many relationships to prevent excess null records and database-bloat. I want to add more entries to a model while excluding existing entries from the model.choice field.
my code looks like this:
the form:
class ExtraAddForm(forms.ModelForm):
def __init__(self, url_kwarg, *args, **kwargs):
super(ExtraAddForm, self).__init__(self, *args, **kwargs)
list_to_exclude = []
query_target = models.Model.objects.get(fk_id=url_kwarg)
for object in query_target:
list_to_exclude.append(object.fk_id.id)
new_queryset = models.Model.objects.exclude(fk_id__in=list_to_exclude)
self.fields['fk_id'].queryset= new_queryset
class Meta:
model = models.Model
fields= ['fk_id','field_b'}
the view:
class AddOjbectsView(FormView):
formset = formset_factory(ExtraAddForm(url_kwarg), can_delete=True)
model = models.Model
url_kwarg = 'url_kwarg'
form_class = formset
template_name = 'some-template.html'
extra_context = {'some_object': models.Model2.objects.all,
'model_object': models.Model.objects.all,
'formset': formset,
'view_type_create': True
}
def __init__(self, *args, **kwargs):
kwargs['url_kwarg']= self.kwargs.get(self.url_kwarg)
super().__init__(self,*args,**kwargs)
def get(self, request, *args, **kwargs):
request.session['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#this works so I'm not re-typing it
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'url_kwarg' not in kwargs:
kwargs['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get_context_data(**kwargs)
#this works, but only in the get_context. Its not working as a solution to my problem.
def get_success_url(self):
#this works, not re-typing
My template has Javascript to handle multiple formsets, and I've tested it with a non-dynamic queryset. The only piece I'm having trouble with is taking the keyword argument from the URL and passing it to the form at init.
Have you tried using FormView.get_form_kwargs method?
Its described in docs
class AddOjbectsView(FormView):
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
form_kwargs['url_kwarg'] = self.kwargs['url_kwarg']
return form_kwargs
It would seem that the answer to my problem is to deprecate the process in favor of session variables at form validation, making use of the clean data functions. My methods above were generated from ignorance of the order of django operations, and shouldn't preoccupy anyone any further.
i have to create multiple object creation on a single API call.My
serializer class is like this
def create(self, validated_data):
dt = datetime.now()
strg = '{:%d%m%Y%H%M%S}'.format(dt, dt.microsecond // 1000)
hat= "REQ" + strg
creater = dot_order_models.DotOrder(reqnum=hat,**validated_data)
creater.save()
return creater
class Meta:
model = dot_order_models.DotOrder
fields = ('merchant', 'biker','batch','customer_name','contact','city','locality','order_amount')
i just given many=true but did not work. then i give
def __init__(self, *args, **kwargs):
many = kwargs.pop('many', True)
super(BikerSerializer, self).__init__(many=many, *args, **kwargs)
this too didn't work.how can i solve it using serializers and view class.
thanks in advance
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list))
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
results = dot_order_models.DotOrder.objects.all()
output_serializer = dot_order_serializers.DotOrderSerializer(results, many=True)
return super(DotOrderViewSet, self).create(request)
I'm using haystack with elasticsearch backend in django, and drf-haystack to implement serializers for results.
I first created a StudentIndex, which indexes over StudentProfiles to use in searches for students in search_indexes.py
class StudentIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StudentProfile
def index_queryset(self, using=None):
return StudentProfile.objects.filter(user__is_active=True)
Which is passed to the serializer and viewset in views.py:
class StudentSerializer(HaystackSerializer):
class Meta:
index_classes = [StudentIndex]
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
and everything was fine and dandy....UNTIL I added two more indexes over other profiles and a single view/serializer to handle them. All exist in the same respective files:
search_indexes.py
class TeacherIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return TeacherProfile
def index_queryset(self, using=None):
return TeacherProfile.objects.filter(user__is_active=True)
class StaffIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StaffProfile
def index_queryset(self, using=None):
return StaffProfile.objects.filter(user__is_active=True)
and added to views.py:
class StaffSerializer(HaystackSerializer):
class Meta:
index_classes = [StaffIndex, TeacherIndex]
class StaffSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StaffProfile, TeacherProfile]
serializer_class = StaffSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
NOW....each view goes to its own url endpoint (/student-search and /staff-search respectively), but ONLY the staff-search endpoint properly returns Staff and TeacherProfile results, while the student-search, in a seperate endpoint with seperate view and models and indexes all explicitly stated, is return StudentProfiles AND Teacher and StaffProfiles and I can not for the life of me figure out why.
If anyone has run into this before, I'd really appreciate help solving, and more importantly, understanding, this issue.
Thanks in advance!
Well, for future people with the same issues, here's all i needed to do to solve it.
I'm using the DRF-Haystack genericviewapi, and on the view, I simply defined the filter_queryset to use the proper haystack connection (which ignored other indices). So for example:
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
queryset = super(HaystackGenericAPIView, self).filter_queryset(queryset)
return queryset.using('student')
Everything now works properly :)
I have the same problem, but when i use the queryset.using('dynamic'), it raises django.core.exceptions.ImproperlyConfigured: The key 'dynamic' isn't an available connection.
class DynamicSearchView(ListModelMixin, HaystackGenericAPIView):
serializer_class = serializers.DynamicSearchSerializer
filter_backends = [HaystackHighlightFilter]
#swagger_auto_schema(
manual_parameters=[text, type]
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
return super(DynamicSearchView, self).filter_queryset(queryset).using('dynamic')
Say I have a simple Django REST Framework view that's extending multiple model classes and serves all the methods in one URL endpoint:
class UserAPIView(RetrieveAPIView, DestroyAPIView, BaseObjectAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = UserSerializer
def get_serializer_class(self, *args, **kwargs):
# return different serializer depending on method??
# return UserUpdateSerializer
return UserViewSerializer
def get(self, request, *args, **kwargs):
"""
Retrieve user details
"""
# ...
return Response(data={'result': "OK"}, status=200)
def delete(self, request, pk):
"""
Delete user
"""
# ...
return Response(data={'result': "OK"}, status=200)
def put(self, request, pk):
"""
Change user
"""
# ...
return Response(data={'result': "OK"}, status=200)
Now I need to use different serializers per method, as my get-method will use different fields than my put-method, example serializers:
class UserViewSerializer(serializers.ModelSerializer):
firstname = serializers.Field(source='firstname')
lastname = serializers.Field(source='lastname')
username = serializers.Field(source='username')
class Meta:
model = User
class UserUpdateSerializer(serializers.ModelSerializer):
firstname = serializers.Field(source='firstname')
lastname = serializers.Field(source='lastname')
class Meta:
model = User
Is it possible to use different serializers for each method in my model based API view?
UPDATE:
I know how to use different serializers inside the methods themselves.
But I need to get the Browsable API generated by Swagger (Django module rest_framework_swagger) to retrieve different serializers for each method.
I can see that loading the API browser page triggers get_serializer_class, but inside that method, I don't know what method Swagger tries to get the serializer for.
How can I get rest_framework_swagger to retrieve different serializers per method?
I think there are at least two ways to achieve this:
You simply set the serializer that you want in each of your methods. Like this:
def get(self, request, *args, **kwargs):
self.serializer_class = UserViewSerializer
# ...
return Response(data={'result': "OK"}, status=200)
You override the get_Serializer_class method. Like this:
def get_serializer_class(self, *args, **kwargs):
if self.request.method == 'POST':
return UserUpdateSerializer
return UserViewSerializer
Hope this helps.
I suppose you could use yaml docstring on each method to override serializers. Like:
def put(self, request, pk):
"""Change user
---
serializer: .serializers.UserUpdateSerializer
"""
# ...
return Response(data={'result': "OK"}, status=200)