I have written two class one for posting datas for payment and other one to show payment-successful message with order_id. I am sending order id from first function and i want to catch this id to show in my payment-successful template.
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self,request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
return HttpResponse(json.dumps({'response':r.json(),'status':'ok'}))
I call this class is ajax and parse there,so if r gives no error then i redirect(window.location=localhost:8000/success) to the success-payment.html page. so response gives me a json data:
{'isSuccess': 1, 'order_id': 1cq2,}
so i want to get this order_id and pass it to another function/class written below.
def payment_successfullView(request):
return render(request,'payment-successfull.html')
How can i achieve so? Thanks in advance.
1. Most simple way
urls.py:
...
path('<str:order_id>/success/', views.payment_successfullView, name='success'),
...
Views:
from django.shortcuts import redirect, reverse
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self, request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
if r.isSuccess:
return redirect(reverse('success', args=(r.order_id, )))
# do your stuff in case of failure here
def payment_successfullView(request, order_id):
return render(request,'payment-successfull.html', {
'order_id': order_id,
})
2. Another method using sessions:
urls.py:
...
path('success/', views.payment_successfullView, name='success'),
...
Views:
from django.shortcuts import redirect, reverse
from django.http import HttpResponseForbidden
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self, request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
if r.isSuccess:
request.session['order_id'] = r.order_id # Put order id in session
return redirect(reverse('success', args=(r.order_id, )))
# do your stuff in case of failure here
def payment_successfullView(request):
if 'order_id' in request.session:
order_id = request.session['order_id'] # Get order_id from session
del request.session['order_id'] # Delete order_id from session if you no longer need it
return render(request,'payment-successfull.html', {
'order_id': order_id,
})
# order_id doesn't exists in session for some reason, eg. someone tried to open this link directly, handle that here.
return HttpResponseForbidden()
Ok, I think the best answer points you in the right direction and let you figure out the fun part.
Tips:
Your APIView has to redirect to payment_successfullView
You have the order_id so you could use a DetailView
If you want to display a list of orders (order_id's) use ListView
I think using those tips, you'll be fine. Happy coding.
Note
You might want to read about Form views also, such view has an attribute called success_url. Ring a bell?
Related
I am trying for some time to test a view of mine that I have protected from access via LoginRequiredMixin and UserPassesTestMixin.
Unfortunately I do not manage to write the appropriate test.
here is the view. The special thing is, that the user must not only be logged in, but he must also belong to the group "Administrator" or "Supervisor".
With this combination I have not yet managed to write a test.
Please who can help me. Here is my View:
class FeatureListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
model = FeatureFilm
template_name = "project/feature-list.html"
def test_func(self):
if self.request.user.groups.filter(name="Administrator").exists():
return True
elif self.request.user.groups.filter(name="Supervisor").exists():
return True
elif self.request.user.groups.filter(name="Operator").exists():
return True
else:
return False
def handle_no_permission(self):
return redirect("access-denied")
and here is a snippet of the url.py:
urlpatterns = [
path("feature/list/", FeatureListView.as_view(), name="feature-list"),
path(
"feature/<int:pk>/date",
FeatureDetailViewDate.as_view(),
name="feature-detail-date",
),
how would you test this FeatureListView and the template belongs to
thank you very much!
First of all it would be better if you would give those groups a permission. Because django offers a lot nice functionalities to check for permissions rather than checking for groups (see here). You would give Administrator, Supervisor and Operator all the same permission and then instead of checking if the user is in one of these groups, you would just check whether the user has that permission. But as that was not the question there you go:
from django.test import TestCase
from django.test import Client
class TestFeatureListView(TestCase):
def _create_administrator(self):
u = Users.objects.create( <input for administrator> )
u.groups.add(administrator)
return u
def _create_supervisor(self):
u = Users.objects.create( <input for supervisor> )
u.groups.add(supervisor)
return u
def _create_operator(self):
u = Users.objects.create( <input for operator> )
u.groups.add(operator)
return u
def _create_normal_user(self):
return Users.objects.create( <input for normal_user> )
def test_users_with_access_rights(self):
users = [
self._create_administrator(),
self._create_supervisor(),
self._create_operator(),
]
for u in users:
c = Client() # initialize every iteration a new client
c.login(username=<username>, password=<password>)
response = c.get(reverse("appwithfeaturelist:feature-list"))
self.assertTemplateUsed(response, "project/feature-list.html")
def test_users_withOUT_access_rights(self):
u = self._create_normal_user()
self.client.login(username=<username>, password=<password>)
response = self.client.get(reverse("appwithfeaturelist:feature-list"), follow=True)
self.assertTemplateNotUsed(response, "project/feature-list.html")
self.assertRedirects(response, "/access-denied/")
def test_without_any_user(self):
response = self.client.get(reverse("appwithfeaturelist:feature-list"), follow=True)
self.assertTemplateNotUsed(response, "project/feature-list.html")
self.assertRedirects(response, "/access-denied/") # this is probably wrong and should test whether the redirect goes to something like a login page
I am creating a simple online shop app so when you want to buy an item, a button click leads you to the charge api. (ex. item 2 will direct to /api/charge/2)
urls.py
from django.urls import path
from gallery.views import ClothingView
from gallery.api.views import ListClothes
from gallery.api.views import ChargeView
urlpatterns = [
path('api/<int:num>/', ListClothes.as_view()),
path('api/charge/<int:num>', ChargeView.as_view(), name='charge_api'),
]
views.py
class ChargeView(ListCreateAPIView):
serializer_class = ChargeSerializer
count = 0
def get_queryset(self):
a = ClothingModel.objects.filter(id=self.kwargs['num']).first()
net_price = int(float(a.full_price) * 100)
if float(a.discount) > 0.00:
net_price = int(net_price * (1 - (float(a.discount) / 100)))
self.count += 1
print(self.count)
if self.count == 1:
stripe.api_key = settings.API_KEY
charge_rtn_body = stripe.Charge.create( # Create charge object
amount=net_price,
currency="usd",
source="tok_visa", # obtained with Stripe.js
description= "[Stripe charge] " + a.description.upper()
)
return ClothingModel.objects.filter(id=self.kwargs['num'])
serializers.py
class ChargeSerializer(serializers.ModelSerializer):
class Meta:
model = ClothingModel
fields = ('full_price', 'discount', 'description')
I am creating a charge object for Stripe (payment method) each time the api gets called, but dependent on the clothing item id. So to handle this I use self.kwargs on get_queryset() to link to a clothing item. When I view the charges on my Stripe dashboard after a single click, multiple charges come at once (4 at a time). I have hard coded the if self.count == 1: as a work around but know that is not good practice. Is there a reason for these multiple calls in get_queryset() per single request and how can I cleanly implement? Thank you.
Objects should be created only in POST requests. get_queryset is called everytime a view is invoked (even for GET requests). So the object create should be moved inside the view function. The dynamic part of the URL is accessible from the view function as present here - https://docs.djangoproject.com/en/2.1/topics/http/urls/#example
class ChargeView(ListCreateAPIView):
def post(self, request, num):
charge = ClothingModel.objects.get(id=num)
from django.conf import settings # new
from django.views.generic.base import TemplateView
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
class HomePageView(TemplateView):
template_name = 'stripe.html'
def get_context_data(self, **kwargs): # new
context = super().get_context_data(**kwargs)
context['key'] = settings.STRIPE_PUBLISHABLE_KEY
return context
def charge(request): # new
if request.method == 'POST':
charge = stripe.Charge.create(amount=5000,currency='ind',description='A Django charge',
source=request.POST['stripeToken'])
return render(request, 'charge.html')
Am using codecoverage and it complains that I have 2 functions in 2 different class based views that are too similar.
Attached is the codecoverage error
Below are the code that were highlighted:
class PalletContentPickup(APIView):
"""
Picking up a pallet content to transfer to exiting pallet content
"""
def put(self, request, pk):
count = request.data['count']
pallet_id = request.data['pallet_id']
from_pallet_content = QuickFind.get_pallet_content_or_404(pallet_content_id=pk)
to_pallet = QuickFind.get_pallet_or_404(pallet_id=pallet_id)
Transfer.validate_if_can_pickup_pallet_content(from_pallet_content, to_pallet, request.user)
to_pallet_content = QuickFind.get_or_create_pallet_content(pallet=to_pallet, product=from_pallet_content.product)
ExitFormHelper.create_exit_form_line_item_on_pallet_content_if_no_exit_form_line(to_pallet_content, request.user)
Transfer.previous_pallet_content_to_new_pallet_content(from_pallet_content, to_pallet_content, count)
serializer = PalletSerializer(from_pallet_content.pallet)
return Response({"data": serializer.data}, status=status.HTTP_202_ACCEPTED)
class PalletContentPutback(APIView):
"""
Put back pallet content to an approved pallet
"""
def put(self, request, pk):
count = request.data['count']
pallet_id = request.data['pallet_id']
from_pallet_content = QuickFind.get_pallet_content_or_404(pallet_content_id=pk)
to_pallet = QuickFind.get_pallet_or_404(pallet_id=pallet_id)
Transfer.validate_if_can_putback_pallet_content(from_pallet_content, to_pallet, request.user)
to_pallet_content = QuickFind.get_or_create_pallet_content(pallet=to_pallet, product=from_pallet_content.product)
ExitFormHelper.create_exit_form_line_item_on_pallet_content_if_no_exit_form_line(to_pallet_content, request.user)
Transfer.previous_pallet_content_to_new_pallet_content(from_pallet_content, to_pallet_content, count)
serializer = PalletSerializer(from_pallet_content.pallet)
return Response({"data": serializer.data}, status=status.HTTP_202_ACCEPTED)
I read about strategy pattern in Python
Not sure if I should apply strategy pattern here and if so, how? Because the example in the url still does not help me realise exactly how to perform strategy pattern here.
As i see it there is only one line of difference between the two classes. so you can keep it really simple by
class PalletContent(object):
"""
Put back pallet content to an approved pallet
"""
def do_action(self, request, pk, action):
count = request.data['count']
pallet_id = request.data['pallet_id']
from_pallet_content = QuickFind.get_pallet_content_or_404(pallet_content_id=pk)
to_pallet = QuickFind.get_pallet_or_404(pallet_id=pallet_id)
if action == 'putback':
Transfer.validate_if_can_putback_pallet_content(from_pallet_content, to_pallet, request.user)
else:
Transfer.validate_if_can_pickup_pallet_content(from_pallet_content, to_pallet, request.user)
to_pallet_content = QuickFind.get_or_create_pallet_content(pallet=to_pallet, product=from_pallet_content.product)
ExitFormHelper.create_exit_form_line_item_on_pallet_content_if_no_exit_form_line(to_pallet_content, request.user)
Transfer.previous_pallet_content_to_new_pallet_content(from_pallet_content, to_pallet_content, count)
serializer = PalletSerializer(from_pallet_content.pallet)
return Response({"data": serializer.data}, status=status.HTTP_202_ACCEPTED)
class PalletContentPickup(APIView, PalletContent):
def put(self,request,pk):
self.do_action(request,pk,'pickup')
class PalletContentPutback(APIView, PalletContent):
def put(self,request,pk):
self.do_action(request,pk,'putback')
You are saving only a few lines of code but it may be worth it when it comes to maintenance. At the same time your validate methods do not seem to return anything. Are they raising exceptions?
def test_post_request_for_api_view(self):
data = {
"email": self.user.email,
}
url = self.reverse('users:the_api', self.user.pk)
response = self.json_post(data, url=url)
self.mock.assert_called_once_with(self.user)
data2 = json.loads(response.content.decode('utf-8'))
self.assertEqual(data2, {
'booking_order': ['ABCDEFGHIJKL'],
'transaction_total': '20000.00'
})
urls.py
urlpatterns = [
url(
regex=r'^$',
view=views.UserListView.as_view(),
name='list'
),
url(
regex=r'^~redirect/$',
view=views.UserRedirectView.as_view(),
name='redirect'
),
url(
regex=r'^(?P<username>[\w.#+-]+)/$',
view=views.UserDetailView.as_view(),
name='detail'
),
url(
regex=r'^~update/$',
view=views.UserUpdateView.as_view(),
name='update'
) ,
url(
regex=r'^the_api/$',
view = views.UserApiView.as_view(),
name='the_api'
),
]
views.py
class UserApiView(APIView):
authentication_classes = authentication.TokenAuthentication ###Am assuming you're authenticating via a token
def get(self, request):
"""
Get user based on username.
Am getting only the username since that's the only field used above.
:param request:
:param format:
:return:
"""
details = User.objects.all()
serializer = UserSerializer(details, many=True)
return Response(serializer.data )
def post(self, request, format=None):
"""
Create a new user instance
:param request:
:param format:
:return:
"""
serializer = UserSerializer(request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
As much as i understand how reverse works in Django, i believe for the above test to pass, i only need to have a the_api url in my urls.py.
However, the test does not pass even with that.
As such, i would love to know what the right thing to do is.
How do i get the test to run?
The error i keep getting is:
django.urls.exceptions.NoReverseMatch: Reverse for 'the_api' with arguments '(1,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['users:the_api/$']
Do you have accepted argument in your url? Looking at your code you should have following entry in your urls.py file:
url(r'the_api/(\d+)/$', your_view, name="the_api")
May be you should paste your url entry too and the corresponding view so that we can look deeper into the issue.
This may be a duplicate question but I can't find any answer here on SO.
I'm trying to write a method able to take two different models. I have a Post model and a Comment model, and I want the vote_up method to handle voting for both of these.
views.py
def vote_up(request, obj): #portotype not working atm...
if isinstance(obj, Post):
o = Post.objects.get(id=obj.id)
elif isinstance(obj, Comment):
o = Comment.objects.get(id=obj.id)
else:
return HttpResponseRedirect(request.META.get('HTTP_REFERER')) #add 'something went wrong' message
o.votes += 1
o.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
urls.py
urlpatterns = patterns('',
url(r'^vote_up/(?P<obj>\d+)/$', 'post.views.vote_up'),
url(r'^post_vote_down/(?P<post_id>\d+)/$', 'post.views.post_vote_down'), # works fine no instance check here, using separate methods for Post/Comment
url(r'^comment_vote_down/(?P<comment_id>\d+)/$', 'post.views.comment_vote_down'),
)
The errors i get is listing my existing urls and:
The current URL, post/vote_up/Post object, didn't match any of these.
or
The current URL, post/vote_up/Comment object, didn't match any of these.
I'm guessing \d+ is the villain but can't seem to find the right syntax.
As Burhan says, you can't send an object in a URL, only a key. But another approach would be to include the model in the URLconf itself: you can use a single pattern, but capture the model name there as well.
url(r'^(?P<model>post|comment)_vote_down/(?P<pk>\d+)/$', 'post.views.post_vote_down'),
)
then in the view:
def vote_up(request, model, pk):
model_dict = {'post': Post, 'comment': Comment}
model_class = model_dict[model]
o = model_class.objects.get(pk=pk)
Change this:
url(r'^vote_up/(?P<obj>\d+)/$', 'post.views.vote_up'),
To
url(r'^vote_up/(?P<obj>[-\w]+)/$', 'post.views.vote_up'),
\d+ means only integer.
You cannot send an object in the URL, you need to send the primary key and then retrieve the corresponding object from the database.
def vote_up(request, obj): #portotype not working atm...
try:
o = Post.objects.get(pk=obj)
except Post.DoesNotExist:
o = Comment.objects.get(pk=obj)
except Comment.DoesNotExist:
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
o.votes += 1
o.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
You need to differentiate between comment and post in your url:
urls.py
url(r'^vote_up/(?P<object_name>\w+)/(?P<id>\d+)/$', 'post.views.vote_up'),
views.py
def vote_up(request, object_name, id):
if object_name == 'comment':
# get id for comment
if object_name == 'post':
# get id for post
else:
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))