How can I call a function within a DRF view? - django

I am using Djano REST Framework for constructing APIs .I want something like below
def addTwoNumber(a,b):
return a+b
class MyView(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
That is , I want a view that doesn't deals with the queryset &serializer_class attributes. Is it possible ? Can anyone help me ?

Why are you using ModelViewSet? Just use APIView http://www.django-rest-framework.org/api-guide/views/
# views.py
from rest_framework.views import APIView
def addTwoNumber(a,b):
return a+b
class MyView(APIView):
def post(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
# urls.py
urlpatterns = [
url(r'^myview/$', MyView.as_view()),
...
]

Related

How to redirect using a view name in Django, class-based views?

I have to redirect from one class-based View to another class-based View. I did it by:
return redirect('report')
but the suggestion was to redirect by View names. I did try this, but it doesn't work.
views.py:
class UploadView(View):
def get(self, request, *args, **kwargs):
Reservation.objects.all().delete()
template = "ReportApp/upload.html"
return render(request, template)
def post(self, request, *args, **kwargs):
# try:
csv_file = request.FILES['file']
data_set = csv_file.read().decode('UTF-8')
# setup a stream which is when we loop through each line we are able to handle a data in a stream
io_string = io.StringIO(data_set)
next(io_string)
for column in csv.reader(io_string, delimiter=',', quotechar="|"):
_ = Reservation.objects.update_or_create(
reservation_code=column[0],
checkin=column[1],
checkout=column[2],
flat=column[3],
city=column[4],
net_income=column[5],
)
# return redirect('report')
return redirect(ReportView.as_view())
upload_view = UploadView.as_view()
class ReportView(View):
def get(self, request, *args, **kwargs):
urls.py:
from .views import upload_view, report_view, city_commission_view, error_view
from django.urls import path
urlpatterns = [
path('upload', upload_view, name='upload'),
path('report', report_view, name='report'),
path('city_commission', city_commission_view, name='city_commission'),
path('error', error_view, name='error'),
]
any suggestions how to do this?
Not sure what you want to achieve and I do not see the code for class ReportView(View): def get(self, request, *args, **kwargs):
but perhaps you could try something like this:
from django.views.generic import RedirectView
class ReportView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
url_params = self.kwargs
and play with that.

Django REST Framework - method patch not allowed with UpdateModelMixin

I would like to update a record in my database with the REST framework. I'm getting an message of method not allowed for anything but "GET".
views.py
class MetadataViewTest(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
urls.py
urlpatterns = [
path('api/metadata_test/<int:pk>/', views.MetadataViewTest.as_view())
]
serializers.py
class SerializerTest(serializers.ModelSerializer):
class Meta:
model = deployment
fields = [field.name for field in deployment._meta.fields]
I've tried the request through postman as a PATCH PUT or POST at api/metadata_test/20/
This is a simplified version, I plan on overriding the get put and delete functions.
Update your code to:
views.py
class MetadataViewTest(generics.RetrieveUpdateDestroyAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
urls.py
urls.py
urlpatterns = [
path('api/metadata_test/', views.MetadataViewTest.as_view()) # Should to the list, not detail
]
Call API like this PUT/PATCH: api/metadata_test/20/
It better if you use viewsets.GenericViewSet instead of generics.GenericAPIView because you are using ...ModelMixin.
Your router like this
from rest_framework import routers
api_router = routers.DefaultRouter()
api_router.register('metadata_test', MetadataViewTest, basename='metadata_test')
...
All you need to do is instead of calling /api/metadata_test/ call /api/metadata_test/12345/
Where 12345 is the id/pk of the record. I am assuming that id is the primary key of the model.

xframe_options_exempt for a Django TemplateView

I am trying to add the decorator #xframe_options_exempt into a django template view but it complais with a
Exception Value: 'dict' object has no attribute
'xframe_options_exempt'
I noticed in Django 1.9 docs the decorator is used for views with a request parameter and I am using a TemplateView.
Is it possible to use it like this?
class MyView(TemplateView):
"""
"""
template_name = 'app/template.html'
from django.views.decorators.clickjacking import xframe_options_exempt
#xframe_options_exempt
def get_context_data(self, **kwargs):
context = {}
context['test'] = 'hello'
return context
Basically I need to embed a django template view into an iframe
When you are decorating class based views, you should use method_decorator. You should override a method that takes the request as an argument, e.g. dispatch (will apply to all request types) or get (will apply to get requests but not post requests). As you found, decorating get_context_data will not work.
class MyView(TemplateView):
#method_decorator(xframe_options_exempt):
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
Note that by using super() you don't have to duplicate the code from TemplateView.
You can decorate the class if you prefer (Django 1.9+)
#method_decorator(xframe_options_exempt, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
Based on the available #xframe_options_exempt decorator, you can also implement a mixin class to be mixed into your view classes:
class XFrameOptionsExemptMixin:
#xframe_options_exempt
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
class SomeView(XFrameOptionsExemptMixin, TemplateView):
…
Well, if anyone else has this problem, this decorator cannot be applied to get_context_data method, but you can override the get method from the TemplateView, something like this:
class MyView(TemplateView):
"""
"""
template_name = 'app/template.html'
from django.views.decorators.clickjacking import xframe_options_exempt
#xframe_options_exempt
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def get_context_data(self, **kwargs):
context = {}
context['test'] = 'hello'
return context
And this will do the trick

Django #login_required for class views

I inherited a Django(1.5.1) project and I need to put one view behind a #login_required decorator. Here is what i have in views.py:
I got this snippet of code from here and it looks like its purpose is to allow someone to apply the #login_requireddecorator to a class
class LoginRequiredMixin(object):
"""
View mixin which verifies that the user has authenticated.
NOTE:
This should be the left-most mixin of a view.
"""
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
class PermissionRequiredMixin(object):
login_url = settings.LOGIN_URL
permission_required = None
raise_exception = False
redirect_field_name = '/workers/'
def dispatch(self, request, *args, **kwargs):
# Verify class settings
if self.permission_required == None or len(
self.permission_required.split(".")) != 2:
raise Error("'PermissionRequiredMixin' requires "
"'permission_required' attribute to be set.")
has_permission = request.user.has_perm(self.permission_required)
if not has_permission:
if self.raise_exception:
return HttpResponseForbidden()
else:
path = urlquote(request.get_full_path())
tup = self.login_url, self.redirect_field_name, path
return HttpResponseRedirect("%s?%s=%s" % tup)
return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs)
I then apply this to the view i want to add permissions to like so:
class RootWorkerView(LoginRequiredMixin, PermissionRequiredMixin, APIView):
renderer_classes = (WorkersJSONRenderer, JSONRenderer,
BrowsableAPIRenderer)
def get(self, request):
worker_list = rest_models.WorkerList(request)
serializer = WorkerListSerializer(worker_list)
return Response(serializer.data)
The APIView argument is a carry over, as before it was the only argument. Is this correct?
When run, I get nothing. The template for the view I want to secure shows up with no login prompt.
Relevant snippet from urls.py:
url(r'^workers/$', views.RootWorkerView.as_view(),
name='root_worker_view'),
url(r'^login/$', 'django.contrib.auth.views.login',
{'template_name': 'dashboard/login.html'}),
/login/ does work, and I can login successful, so that's not the issue.
I feel like #method_decorator(login_required) isnt doing its job. Any ideas?
You can add the decorator in the urls.py
from django.contrib.auth.decorators import login_required
url(r'^workers/$', login_required(views.RootWorkerView.as_view()))
This worked for me.
now you can use Django builtin LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
https://docs.djangoproject.com/en/3.2/topics/auth/default/#the-loginrequired-mixin

django Initialising Choices in Form from Class Based View

I've seen a lot of answers about how this is solved when not using Class Based Views. Is there some really obvious thing I'm missing about doing it with CBVs?
Basically I want to have a MultipleChoiceField in my form which has choices determined by what is happening in the view. e.g. I use the PK from the URL to do some backend requests and then those should be used to populate the choices.
# forms.py
from django.forms import Form, MultipleChoiceField, CharField
class EmailForm(Form):
users = MultipleChoiceField(required=False)
subject = CharField(max_length=100)
message = CharField()
def __init__(self, users=None, *args, **kwargs):
super(EmailForm, self).__init__(*args, **kwargs)
if users:
self.fields['users'].choices = users
#urls.py
from django.conf.urls import url, patterns
from .views import EmailView
# url patterns
urlpatterns = patterns('',
url( r'^(?P<pk>\d+)$', EmailView.as_view(), name="maindex" ),
)
#views.py
from django.views.generic import FormView, TemplateView
from .forms import EmailForm
class EmailView(FormView):
template_name = 'myapp/email.html'
form_class = EmailForm
success_ulr = '/thanks/'
def form_valid(self, form):
# Do stuff here
return super(EmailView, self).form_valid(form)
Basically it boils down to how/where to call the init function from the view. How do I do that? Or is there another way I've missed? I thought of overriding get_form_kwargs in the view, but couldn't make that do anything.
Thanks
The view:
from django.views.generic import FormView
class EmailView(FormView):
# ...
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['users'] = users
return kwargs
The form:
from django import forms import Form
class EmailForm(Form):
users = MultipleChoiceField(required=False)
# ...
def __init__(self, *args, **kwargs):
self.users = kwargs.pop('users', None)
super(EmailForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = self.users
Basically, what I've done in a similar case is the following (Python 3.5, Django 1.8):
def get_form(self, *args, **kwargs):
form= super().get_form(*args, **kwargs)
form.fields['rank'].choices= <sequence of 2-tuples>
return form
where obviously rank is the field name. This way I use the default form.
Alright, the FormMixin calls get_form to get the form-class which looks like
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
return form_class(**self.get_form_kwargs())
So you can either override get_form to instance your Form yourself
def get_form(self, form_class):
return EmailForm(files=self.request.FILES or None,
data=self.request.POST or None,
users=some_user_queryset)
or stay a bit more generic and override get_form_kwargs to something like
def get_form_kwargs(self):
form_kws = super(EmailView, self).get_form_kwargs()
form_kws["users"] = some_user_queryset
return form_kws
One way to do it is:
class EmailView(FormView):
# ...
def get(self, request, *args, **kwargs):
self.users = ...
return super(EmailView, self).get(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
kwargs['users'] = self.users
return kwargs
This allows you to set the user choices in the view and to pass them to the form.
You can override get_form.
I needed to update the choices for ChoiceField based on logged in user.
Form:
class InteractionCreateForm(forms.Form):
device = forms.ChoiceField(choices=[(None, '----------')])
...
View:
class InteractionCreateView(FormView):
form_class = InteractionCreateForm
...
def get_form(self, form_class=None):
form_class = super().get_form(form_class=None)
form_class.fields['device'].choices = \
form_class.fields['device'].choices \
+ [(device.pk, device) for device in Device.objects.filter(owner=self.request.user.id)]
return form_class