api_view['POST', 'GET'] against SOLID principles? [closed] - django

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 months ago.
Improve this question
It's not a problem I'm facing its just an open discussion.
in Django Rest framework we declare a decorator #api_view[] and it take an argument like
POST
GET
PUT
DELETE
in my case I'm allowed to put more than one argument in the decorator and make one function made more than one responsibility like
#api_view['POST', 'GET]
def fbv_list(request):`
if request.method == 'POST':
#do something
if request.method == 'GET':
#do something
in this case fbv_list make more than one thing which is POST and GET
isn't in this case this function is against the Single responsibility class which is from SOLID principles ????
That's my question and if yes what is supposed to do to avoid breaking the SOLID .
Thanks in advance.

I will start my answer by quoting the definition of a view from Django's official docs:
A view function, or view for short, is a Python function that takes a
web request and returns a web response. This response can be the HTML
contents of a web page, or a redirect, or a 404 error, or an XML
document, or an image . . . or anything, really. The view itself
contains whatever arbitrary logic is necessary to return that
response.
The responsibility of the view is to take a web request as input and return a web response as the output. DRF APIs are also "views", so this definition fits there as well.
Breaking it down further, the core responsibility of the view is to take a request and perform "arbitrary" logic on it. What's arbitrary? When a view receives a request, how will it decide what set of operations are to be performed? Is the request attempting to get some information from the system, create a record, or maybe try a complex update operation on the data models? That's what the view will have to decide because that is its primary purpose. Based on the nature of the input request, the type of output will change. However, the core functionality is still the same. It does not violate SRP. I am also listing one of the code snippets from Django docs that is using the same pattern:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
P.S. Do check this link out to see how SRP can be misleading and how its purpose and usage are subjective.

In my opinion you are not breaking any rules. With one function you are handling one object. Doing so, it saves you lines/typing, which makes easier later to read and debug. Breaking the rules would be if your get handles one object and post handles another. It would be hell to debug that if something is not working.
(My opinion).

Related

Is there a more elegant way to write this django view?

I've been messing around with django and I have this django view:
def handle_results(request):
if request.method == "POST" and request.is_ajax():
# Do something with the post request
elif request.method == "GET" and request.is_ajax():
# Do something with the get request
else:
# First time in this view, render first element to display
return render(
request, "result_page.html", context={"display": arr[0]}
)
The main idea is, this is supposed to be a Same Page Application, and the first time I'm in this view, I need to render the contents of the array to display to the user, after that, the user can interact with said array via the html (think of it as upvoting or downvoting stuff that's shown). Depending on the user's choice, I get a GET or POST request and need to deal with said request.
However, the way I'm implementing this seems not that elegant and I was wondering if there'd be another better way to accomplish what I'm doing.
Thank you so much!
I would suggest using a class based view

Django is there a benefit of checking every view with `method=='POST':`

Is there a benefit to starting every one of my view functions with if request.method=='POST': or if request.method=='GET':? Or would I just be adding unnecessary lines of code?
I've followed a few examples where views for Ajax are all checking if the HTTP is made with GET.
Could it, for example, prevent DDOS from latching on to a POST method and hammering it with GETs? Or, more practically, prevent API consumers from incorrectly PATCHing when they should PUT or POST?
def employee_delete(request, uid):
if request.method == 'DELETE':
def employee_detail(request, uid):
if request.method == 'GET':
def employee_create(request):
if request.method == 'POST':
def employee_update(request, uid):
if request.method == 'PUT':
Is there a benefit to starting every one of my view functions with if request.method=='POST':
Yes, even if you only support one method, it is better to guard this. The HTTP protocol specifies that GET requests, should not have side-effects (well effects in the sense of counting visitors are probably no problem, but not something that changes the "entities" of your business logic is strictly speaking not acceptable).
Now "web crawlers" (for example used by search engines or scrapers) typically detect links on a page, and make GET requests on these links (since they aim to "discover" new pages). If there is a view behind this URL that, for example, deletes an employee, it can happen that accidentally a "web crawler" will edit your database.
Other methods like GET, HEAD, PUT and DELETE should be idempotent (that means that making the same request twice, should have the same side-effects, as making the request only once).
So by not "protecting" your views, you lose a layer of "protection" against accidental misuse of your webserver.
A hacker could also aim to make a request with another method, and see how the server responds in a search to find exploits: for example, look if a server makes certain assumptions on the method that fail when performing a DELETE request. For example, a rather generic view implementation that handles all methods, could - if not guarded - unintentionally allow the deletion of files (for example you write a generic view to create and edit content can be "misused" by a hacker by using a DELETE request that the parent view implemented, but should not be supported for that specific entity).
In the early days, some HTTP webservers for example did not check authentication when a HEAD request was used. As a result, a hacker could, by trying several HEAD requests "scan" the id space, and thus obtain knowledge what id's were filled in in the database. Of course that in itself does not leak much data, but it is a vulnerability that can be used as a first step in hacking data.
Note that although Django has some protection against this when using, for example, class-based views, a person can just use any string for the request. So a person can write as method FOOBAR. If the view for example specifies if request.method == 'POST', and an else: statement, it can thus be used, to enter the else statement with a non-GET method.
But regardless of the use-case, "better be safe than sorry", and guarding the HTTP methods, is just one of the aspects to check.
That being said, if only a subset of methods are allowed, you can use the #require_http_methods [Django-doc] decorator:
from django.views.decorators.http import require_http_methods
#require_http_methods(["GET", "POST"])
def my_view(request):
# I can assume now that only GET or POST requests make it this far
# ...
pass
This decorator thus makes it more elegant to guard that the proper method is used.
To offer a different perspective, I think your question illustrates why you should consider using class-based views, which make life so much simpler when dealing with such problems.
For example the generic CreateView already comes with all the logic built in to restrict the type of HTTP request. It will let you perform a GET request to initialise a form, but require a POST request to process data. Thus you can't accidentally trigger data to be saved through a GET request.
It also provides the framework for proper form data validation, error handling etc - which you would have to implement yourself in a procedural view.
Same goes for the range of other views that Django provides - UpdateView, DetailView etc.
All Django class-based views come with a http_method_names attribute that you can use to control which methods are allowed on your views, e.g.,
from django.views.generic import View
class MyView(View):
# only GET and POST allowed. Anything else will get a 405 Method Not Allowed response.
http_method_names = ['get', 'post']
def get(self, request, *args, **kwargs):
# Logic for GET requests goes here.
def post(self, request, *args, **kwargs):
# Logic for POST requests goes here. No risk of it getting mixed up with GET.
This in addition to providing a lot of other helpers for things like form handling, template loading etc. Procedural views may feel simpler initially, but you will quickly realise that you end up having to write a lot more code to get them to do what you need.

payment processing with django

I am trying to build code that allows people to pay for the services that they get from my website. I have built the form using the forms.Form model available in django. I have also used the following pattern to build my view.
if request.method == 'POST'
form = ContactForm(request.POST)
if form.is_valid(): # All validation rules pass
conn = urllib2.Request(payment_gateway_url,urllib.urlencode(my_dat_in_dict))
f= urrlib2.urlopen(conn)
all_results=f.read()
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {
'form': form,
})
The problem i am facing is that my forms get submitted OK and the payment gateway responds back to me with a whole bunch of html that helps the user to choose the credit card details etc in the response to this form POST(details that i am not collecting on my website). I can see this in the all_results (f.read()).
My question is that how do i show the user this page, since i get this as a result of my form POST. Should i save this response in a html file and HTTPResponseredirect to that page. I am assuming that HTTPResponseRedirect is more for a complete transaction and not the intermediate responses.
So, basically how do i handle the situation where the user will get sent to the payment gateway site and then after completing the proceedings come back to my site?
thanks
First off, I would say, if you live in the US, check out Stripe (or something similar). The way you are describing your payment process seems a bit overly complicated.
With that said, if (and I doubt this is the case), the HTML returned from the most is in the correct format for display on your website, you can just stick it in to an HttpResponse (must be a sprint)
return HttpResponse(html)
Otherwise, use something like BeautifulSoup, Scrape.py, or something similar to format it IN RAM, and then use HttpResponse. I would not write it to the file system for modification.

Django practice: smaller views or less views?

I'm developing an app with django, and I have a view where I use 2 return render_to_response, with two different html files, depending on the status of the user.
I was wondering if it would be a better practice to split my view into two different views or if I should keep a bigger view.
What are the pros and the cons of doing so?
Sorry if my question is not clear. Thank you very much for your advices.
There's no right or wrong answer to this question so your question may not be acceptable on stackoverflow, which usually is intended for questions/problems with specific technical solutions.
That said, here's my view on this topic - I personally like to keep my view function small and if further processing is required, split them out into smaller functions.
For example:-
#permission_required('organizations.organization_add_user')
def organization_add_user(request, org_slug):
org = get_object_or_404(Organization, slug=org_slug)
form = OrganizationAddUserForm(org=org)
if request.method == 'POST':
form = OrganizationAddUserForm(request.POST or None, request.FILES or None, org=org)
if form.is_valid():
cd = form.cleaned_data
# Create the user object & send out email for activation
user = create_user_from_manual(request, data=cd)
# Add user to OrganizationUser
org_user, created = OrganizationUser.objects.get_or_create(user=user,\
organization=org)
dept = org.departments.get(name=cd['department'])
org_user.departments.add(dept)
# Add user to the appropriate roles (OrganizationGroup) and groups (django groups)
org_groups = OrganizationGroup.objects.filter(group__name__in=cd['roles'], \
organization=org)
for g in org_groups:
user.groups.add(g.group)
return HttpResponse(reverse('add_user_success'))
template = 'organizations/add_user.html'
template_vars = {'form': form}
# override request with dictionary template_vars
template_vars = FormMediaRequestContext(request=request, dict=template_vars)
return render(request, template, template_vars)
FormMediaEquestContext is a class I import from another file and has its own logic which helps me to handle javascript and css files associated with my form (OrganizationAddUserForm).
create_user_from_manual is yet another function which is encapsulated separately and deals with the reasonably convolutated logic relating to creating a new user in my system and sending an invitation email to that new user.
And of course, I serve up a different template if this is the first time a user arrives on this "add user" page as opposed to redirecting to a completely different url with its own view function and template when the add user form is successfully executed.
By keeping our view functions reasonably small, I have an easier time tracking down bugs relating to specific functionality.
In addition, it is also a good way to "reuse" my utility functions such as the create_user_from_manual method should I need this same utility function in another view function.
However, at the end of the day, organizing code and encapsulating code is a judgement call that you get to make as you progress as a developer.

Django view: good programming practice question

suppose that you have a webpage that uses the post and get method, and you wrote the following view to generate it:
def homepage(request):
if 'login' in request.POST:
# ......... code goes here, and you return an appropriate response
if 'register' in request.POST:
# ......... code goes here, and you return an appropriate response
# When no Post request, just render the page
return render_to_response('homepage.html')
Question:
Is it considered good programming practice to split the above view into three views: one for login, one for register and one that would render the page? Or is it OK to keep it as it is.
EDIT
in the case listed above, I am not checking if the server received a "GET". But the idea is still the same :)
I think a better idea would be to have each of the two forms submit to a different URL. Then you can define views for, e.g., /login, /register, and / (the default homepage handler). Your views for /login and /register can use if request.method == 'POST', and then redirect to the homepage if they are called with a GET request.