I have a Django viewset, where I have a function, that sends an email, whenever the viewset, is used.
I override the create function, to send the email, where I have a function, that sends the email to the user.
I want to pass in some arguments, to the function, to display in the email (done with Django's template engine)
class ContactRequestViewSet(viewsets.ModelViewSet):
queryset = ContactRequest.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = ContactRequestSerializer
def create(self, request, *args, **kwargs):
response = super(ContactRequestViewSet, self).create(request, *args, **kwargs)
send_email()
return response
#function to send email
def send_email():
htmly = get_template('email.html')
d = {'company_name': 'dodo'} #i want this dictionary, to contain the attributes from the viewset
send_mail(
subject='Test email',
message='',
from_email='test#email.com',
recipient_list=['test#email.com'],
html_message= htmly.render(d)
)
right now I just have a sample placeholder as d but here I want to pass in attributes from the serializers/model, that the user provided, I tried passing in the serializer, and accessing its attributes, but I don't know how to do this the proper way
You can access response.data after this line
response = super(ContactRequestViewSet, self).create(request, *args, **kwargs)
which will hold the serializer's data. Yet if you want an actual instance of your model this will not be sufficient and you will need to hack your way around a little bit. The CreateModelMixin that is used in ModelViewSet of django-rest-framework has the following methods:
class CreateModelMixin:
# ... more stuff here
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
so you could override the perform_create method to save the instance into an attribute of your ContactRequestViewSet like this:
def perform_create(self, serializer):
self.instance = serializer.save()
then in the create method you could do something like this:
def create(self, request, *args, **kwargs):
response = super(ContactRequestViewSet, self).create(request, *args, **kwargs) # This calls perform_create internally
send_email(self.instance)
return response
Related
I want to override CreateModelMixin with my own class which derived from ModelViewSet
this is the class i want to override
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
in the first instinct, I want to override only the create function with a different serializer
but would it be more correct to override perform_create?
if someone can guide me how it is the most correct way to override that will be much of help
thank you
I have the following view:
class PersonalInfos(generics.RetrieveUpdateAPIView):
serializer_class = ClientSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""
:return: A QuerySet Object
"""
return Client.objects.get(user=self.request.user)
def get(self, *args):
"""
:param args: Handled by rest_framework views.dispatch
:return: JSON object containing User Personal Data
"""
queryset = self.get_queryset()
serializer = ClientSerializer(queryset)
return Response(data=serializer.data)
def patch(self, request):
"""
:param request: request object is sent by the client
:return: Json response with the data sent of the body
"""
client = self.get_queryset()
serializer = ClientSerializer(client, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(data=serializer.data, status=200)
return Response(data="Unexpected Parameters", status=400)
Everything works fine in the view, but the problem is that I am using drf-spectacular and it is showing me a PUT method in the documentation that we won't be needing in the API. My questions is, how can I customize drf-spectacular to not include a PUT method in the documentation?
You may use the #extend_schema decorator to exclude one or more methods from the schema generated, as shown below.
#extend_schema(methods=['PUT'], exclude=True)
I solved this using RetrieveAPIView instead of UpdateRetrieveAPIView and I have extended it with to include a PATCH method. RetrieveAPIView will handle a PATCH method perfectly and would not show automatically an UPDATE request in the API documentation. Here is the new code:
class PersonalInfos(generics.RetrieveAPIView):
serializer_class = ClientSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""
:return: A QuerySet Object
"""
return Client.objects.get(user=self.request.user)
def get(self, *args):
"""
:param args: Handled by rest_framework views.dispatch
:return: JSON object containing User Personal Data
"""
queryset = self.get_queryset()
serializer = ClientSerializer(queryset)
return Response(data=serializer.data)
def patch(self, request):
"""
:param request: request object is sent by the client
:return: Json response with the data sent of the body
"""
client = self.get_queryset()
serializer = ClientSerializer(client, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(data=serializer.data, status=200)
return Response(data="Unexpected Parameters", status=400)
this should exclude the put method in drf-yasg
class MyView(generics.RetrieveUpdateAPIView):
#swagger_auto_schema(auto_schema=None)
def put(self, request, *args, **kwargs):
return
source: https://drf-yasg.readthedocs.io/en/stable/custom_spec.html#excluding-endpoints
class ItemListView(ListCreateAPIView):
model = Item
serializer_class = ItemSerializer # model serializer
def get_queryset(self):
return self.model.objects.all()
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
Is there any chance to add additional data to JSON response in get method?
You could override ItemSerializer's to_representation() method, as
class ItemSerializer(serializers.ModelSerializer):
# your fields
def to_representation(self, instance):
data = super().to_representation(instance).copy()
data.update({"key": "value"})
return data
response = self.list(..)
response.data['hello'] = 'world'
return response
I am using Django 1.11 and DRF 3.6.2 and just started developing an API...
I am trying to check what are the changes to be performed in the database with the data being sent.
class IndividualViewSet(viewsets.ModelViewSet):
"""Individual ViewSet."""
serializer_class = serializers.IndividualSerializer
queryset = models.Individual.objects.all()
def update(self, request, equipment_serial, pk=None):
queryset = models.Individual.objects.get(pk=pk)
serializer = serializers.IndividualSerializer(queryset, data=request.data["entities"][0])
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status.HTTP_200_OK)
return Response(status.HTTP_400_BAD_REQUEST)
def perform_update(self, serializer):
old_obj = self.get_object()
new_data_dict = serializer.validated_data
if old_obj.name != new_data_dict['name']:
# logic for different data
# ...
serializer.save()
However, with the code as above, the perform_update function is never being called by serializer.save() on the update function.
According to the docs, ModelViewSet is inherited from GenericAPIView and it has UpdateModelMixin, which should automatically calls the perform_update function before saving
My questions surrounds on why it is happen and how should I do in order to accomplish the desired behaviour.
This is because you are overriding the update method in your custom viewset. This is the original code for the UpdateModelMixin that the ModelViewSet mixes in:
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# refresh the instance from the database.
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
In this original version, perform_update is called whenever in the update method. If you override that method and still want to call perform_update, you need to put it there.
I'm trying to use validation with the request.user object to restrict updates to some rows for specific users within the django admin site. I get the impression I need to override the ModelAdmin change_view method to pass the request object to the form. I've looked at the change_view method in django.contrib.admin.options, but as someone very new to django, am having trouble understanding where in the change_view method I need to make these modifications. Any pointers in the right direction would be great.
class IssuesAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None):
#modify lines to pass request to form
form = IssuesAdminForm
class IssuesAdminForm(forms.ModelForm):
class Meta:
model = Issues
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(IssuesAdminForm, self).__init__(*args, **kwargs)
def clean_product(self):
if self.request.user.name=='someone'
return self.cleaned_data["product"]
else:
raise forms.ValidationError("Nope!")
class IssuesAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None): #remember to edit also add_view()... etc
self.form.request = request
form = IssuesAdminForm
class IssuesAdminForm(forms.ModelForm):
class Meta:
model = Issues
def __init__(self, *args, **kwargs):
self.request = # do what you need ;)
super(IssuesAdminForm, self).__init__(*args, **kwargs)
def clean_product(self):
if self.request.user.name=='someone'
return self.cleaned_data["product"]
else:
raise forms.ValidationError("Nope!")