Im trying to implement Stripe Payment API into my django site.
From my cart.html page I have a button that goes to my checkoutView and creates Products and Prices from the cart:
def checkout(request):
customer = request.user.customer
order = Order.objects.get(customer=customer)
items = order.orderitem_set.all()
prices =[]
for item in items:
product =stripe.Product.create(
name = item.product.name,
description= item.product.description
)
price = stripe.Price.create(
product= product.id,
unit_amount=int(item.product.price.amount)*100,
currency='gbp',
)
prices.append(price.id)
line_items=[]
for item, price in zip(items,prices):
line_items.append({'price':price,'quantity':item.quantity}),
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=line_items,
mode='payment',
success_url='http://127.0.0.1:8000/SUCCESS/?session_id={CHECKOUT_SESSION_ID}',
cancel_url='http://127.0.0.1:8000/'
)
I then have to redirect to Stripes checkout Docs here. I would like to call this from a button on my cart but im not sure how to get the SessionId from the view BACK to the template and call this.
Any help would be greatly appreciated.
Typically you would pass the Session ID into your view, or use an asynchronous request to create the session from your backend. For example:
from django.shortcuts import render
def build_checkout_session(customer):
# Use your existing code to create Products, Prices, etc...
session = stripe.checkout.Session.create(…)
return session
def checkout_view(request):
session = build_checkout_session(customer=request.user.customer)
return render(request, 'checkout.html', {'session_id': session.id})
Then, in your checkout.html:
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
const sessionID = "{{ session_id }}";
const stripe = Stripe("pk_test_12345"); // Put your publishable key here
document.getElementById('checkoutButton').addEventListener('click' (evt) => {
evt.preventDefault();
stripe.redirectToCheckout({sessionId: sessionID});
});
</script>
I wanted to note also that rather than create Products and Prices each time, you can pass ad-hoc data into the Checkout Session as shown here: https://stripe.com/docs/payments/checkout/accept-a-payment#creating-ad-hoc-prices
Related
I have this code that enables you to vote for an article, right now a user can vote unlimited times I want to make it so when people click the button first time it gets the value by one and then decreased by one and so forth.
here's article.html:
<button id="vote">vote</button>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$("#vote").click(function (e) {
e.preventDefault()
var upvotes = $("#total_votes").html()
var updatedUpVotes = parseInt(upvotes) + 1
$("#total_votes").html(updatedUpVotes)
$.ajax({
url: 'vote/',
method: "GET",
data: {},
success: function (data) {
console.log(data)
},
error: function (error) {
console.log(error)
}
})
})
</script>
vote function in views.py:
def vote(request, article_id):
article = get_object_or_404(Article, pk=article_id)
article.votes += 1
article.save()
return JsonResponse(data = {"vote": "Voted! Thank you for the vote."})
Create a separate model that will contain article id and user id. When click on like button check whether data exists for that user and article. If it exists, decrease the vote by 1 else increase its value.
You need to have a separate model to store Vote information attached with user. Otherwise you will not have any idea who voted that article. You can try something like this:
# Model
class Vote(models.Model):
user = models.ForeignKey(User,on_delete=models.DO_NOTHING)
article = models.ForeignKey(Article,on_delete=models.DO_NOTHING)
# View
from django.contrib.auth.decorators import login_required
#login_required
def vote(request, article_id):
article = get_object_or_404(Article, pk=article_id)
vote, created = Vote.objects.get_or_create(article=article, user=request.user)
if created:
return JsonResponse(data = {"vote": "Voted! Thank you for the vote."})
return JsonResponse(data = {"vote": "You already voted for this article."})
FYI: even though GET request will work, but I would recommend using POST method, because as per MDN:
Requests using GET should only retrieve data.
But clearly this method will change in Database.
I'm implementing a website and I need to save in database some variables that comes from HTML. The idea is save the paragraphs of the text that the user marked and save on database to show it when the user access the page (like in medium.com).
When the user click on paragraph I can't refresh the page, I just need to save on database the paragraph id (data-artigo) that was clicked.
That's my view details.html and I need to save in the database the values artigo.pk and lei.pk
<!-- Begin Post Content -->
<div class="article-post">
{% for artigo in artigos %}
<p class='artigo' data-artigo = "{{artigo.pk}}" data-lei = "{{lei.pk}}">
{{artigo}}
</p>
{% endfor %}
</div>
<!-- End Post Content -->
I have a js function that receive those values and set a yellow background (.highlight) to mark the paragraph that was clicked. So I have to save those data in database:
$("p.artigo").on("dblclick",(function(e){
let artigo = $(this).data('artigo');
let lei = $(this).data('lei');
let is_marked;
if ($(this).hasClass( "highlight" )){
$(this).removeClass("highlight");
is_marked = false;
}else{
$(this).addClass("highlight");
is_marked = true;
}
}));
That is the table (my model) when I need to store those data:
class Marcacao(models.Model):
lei = models.ForeignKey(Lei, on_delete=models.CASCADE, verbose_name='Lei', related_name='marcacaoArtigos')
artigo = models.ForeignKey(Lei, on_delete=models.CASCADE, verbose_name='Artigo', related_name='marcacaoLei')
usuario = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='marcacaoUsuário', related_name='marcacaoUsuario')
is_marked = models.BooleanField('Está Marcado?', blank=True, default=False)
description = models.TextField('Descrição', blank = True, null=True)
If I'm not wrong, I think that I need to create a function in the view and pass those data as parameter to this function, but I don't know how to do it.
Your best bet with this depends on how you want this to work.
If the user clicking a paragraph is considered a 'submit' perhaps run an Ajax query from the JS to the view and POST the data back to be put into the model.
However if you want to continue activity on the page consider using Django Rest Framework and creating an API call you can again send off data in an Ajax query to.
My suggestion would be DRF, it's pretty simple to serialize data and save into the model.
https://www.django-rest-framework.org/
You register your api in urls.py:
router = routers.DefaultRouter()
api_patterns = [
router.register(r'selected-paragraph', api.selectedParagraphViewSet)
]
urlpatterns = [
url(r'api/', include(api_patterns)),
Then in your api.py:
class selectedParagraphViewSet(viewsets.ModelViewset):
queryset = Marcacao.objects.all()
serializer_class = MarcacaoSerializer
def create(self, request, *args, **kwargs):
try:
data = {
'lei': request.data.pop('lei'),
'artigo': request.data.pop('artigo'),
'is_marked': request.data.pop('is_marked'),
'usuario': request.user
}
serializer = self.get_serializer(data=data, method='post')
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
except serializers.ValidationError:
print(traceback.print_exc())
raise
Then in your serializers.py:
class MarcacaoSerializer(serializers.ModelSerializer):
def Meta:
model = Marcacao
fields = '__ALL__'
And finally a nice ajax call to send it all off:
var submitData = {
'let': lei,
'artigo': artigo,
'is_marked': is_marked
}
$.ajax({
url: '/api/selected-paragraph',
method: 'PUT',
data: submitData,
dataType: 'json',
crossDomain: true,
xhrFields: {
withCredentials: true
},
success: function(data, stat, xhr) {
console.log("Was a success");
},
failure: function(xhr, stat, err){
console.log('POST error');
console.log((xhr.responseJSON && xhr.responseJSON.msg) ? xhr.responseJSON.msg : '"'+err+'" response when communicating with server.');
}
});
Also good to note is you can eliminate even the need for the create function in api.py if you can pass the request.user in the Ajax call, but I figured this way would show better how the data is input into the database.
I'm looking at building a site where you have a detail view of an object, and more data about that object can be shown to the user by clicking to open a modal pop up box. I was wondering how, in Django, this data can only be loaded if the user chooses to open the modal box - otherwise there's no point in it being there.
def detail_view(request):
...
extra_data = Object.objects.all().values_list('rating', flat=True) # Only required if a user open the modal box
return render(request, 'detail.html', {'extra_data':extra_data})
Any ideas on how this might be achieved whilst using as little JavaScript as possible?
This is a very broad question, although the following is a generic structure of how you can achieve this. The code below is just for reference. It is just to demonstrate the structure. You need two views, first view will fetch the basic info for all items. The second view fill fetch the additional details for the selected item.
The assumption is that you will have a button for opening the modal, to show the more details.
The javascript is listening to the click event on that button, and it is fetching more details that is to be displayed from the server, and then displaying it in the modal container.
Disclaimer: This is not the most optimal way to do this, this is just a quick and dirty solution.
//Assuming there is a button with id myButton using which user will toggle modal
$("#myButton").on("click", function(e) {
e.preventDefault();
var modal = null;//whatver the modal is
var model_id = 1;//store the id of the model in a accessible location and load it here
var modalContainer = $("#modalContent") // the element which is the container of the modal which will hold its contents
$.ajax({
url: "/get_item_detail", //this url should call viewB
type: "GET",
data: {
"id": model_id
},
success: function(response) {
var html = "<div></div>" //generate your html content for the modal container
modalContainer.html(html); //put the generated html content inside the modal Container
modal.open(); //open the modal here
},
error: function(response) {
}
})
});
from django.shortcuts import render
from django.http import JsonResponse
import json
def viewA(request):
#Fetch the basic info for all objects
items = Sample.objects.all()
return render(reqeust, "template.html", {
"items": items
})
def viewB(request):
#Fetch the additional detail of the concerned object
id = request.GET.get("id", None) #get id or if no id, set None
detailed_item = Sample.objects.get(id=id)
return JsonResponse(json.loads(detailed_item), safe=False)
I'm trying to implement a search bar to query my database and show only the matches. When I hit submit it just gives me back 'SEARCH', which is what I set as the default instead of printing an error.
ajax.py
...
def chunkSearcher(request):
test = request.GET.get('search_box', "SEARCH")
print(test)
....
Searcher.html
<form type="get" action="." style="margin: 0">
<input id="search_box" type="text" name="search_box" value="Search..." >
<button id="search_submit" type="submit" >Submit</button>
urls.py
url(r'^ajax/chunk/Searcher/$',
ajax.chunkSearcher, name='chunkSearcher')
views.py (It actually works here for some reason but it won't recognize the same two lines of code in my ajax code
def searcher(request):
# test = request.GET.get('search_box', "SEARCH")
# print(test)
this_main = Searcher(
request = request,
num_elements = Candidate.objects.all().count(),
size = 'col-xs-12',
title = 'Search',
modelname = 'Searcher',
listing_fields = [
{'readable_name': 'Name', 'model_attribute': 'full_name()', 'subtext_model': 'email', 'color': 'False'},
{'readable_name': 'Status', 'model_attribute': 'get_status_display()', 'color': 'True'},
{'readable_name': 'Automated Status', 'model_attribute': 'get_auto_status()', 'color': 'True'},
{'readable_name': 'Submitter', 'model_attribute': 'submitter', 'color': 'True'},
],
listing_actions = [
{'tooltip': 'Search', 'color': 'success', 'icon': 'plus', 'permission': 'prog_port.add_candidate', 'modal': 'candidateform', 'controller': 'addCandidate'},
],
)
context = {
'nav' : Nav(request),
'main' : this_main,
'fb' : TestFeedback()
}
return render(request, 'prog_port/base.html', context)
widgets.py
class Searcher:
def __init__(self, request,
num_elements,
size = 'col-xs-12',
modelname = None,
title = None,
listing_fields = None,
listing_actions = None):#!!
self.template = 'prog_port/widgets/Searcher.html'
self.size = size
self.modelname = modelname
self.num_elements = num_elements
self.num_pages = int(math.ceil( num_elements / 25.0))
self.title = title
self.listing_fields = [x['readable_name'] for x in listing_fields]
self.listing_actions = listing_actions
for action in self.listing_actions:
action['restricted'] = False
if 'permission' in action:
if not request.user.has_perm(action['permission']):
action['restricted'] = True
Getting this working without Ajax would be a bit quicker to start. When the action attribute of your form is pointed towards the URL of the current page (rather than towards the URL of your ajax view), the GET request is sent to the view that corresponds to that page's URL - your searcher view in your case. That's why you were able to get the expected values to print when you had those two lines in that view.
Importantly, since the searcher view is the one rendering your page, having access to your search_box value in that view lets you filter or otherwise manipulate the queryset being passed into the view's context and ultimately display only the restricted/filtered items you want shown.
A separate Ajax view doesn't have access to all of that stuff right off of the bat. To dynamically update your search results with a separate Ajax view, that view will need to respond to your request with all of the information necessary to re-render the page appropriately. Practically speaking, that usually means one of two things:
Your search results are displayed within a div or other defined content area, and your Ajax view returns the HTML necessary to populate that content area with the appropriate stuff, or
Your initial view renders its template based on some serialized JSON, and your Ajax view provides updated information in that format which is then used to re-render the template.
This is a good starting point for getting the hang of ajax with django. Notice in the example code given how the view responds to the ajax call with some data (a HTTPResponse or a rendered template), and how that data is then used in the success/failure functions.
If your ajax view returned the HTML necessary to render search results, you could use your success function to update the search results div (or table or whatever) on your page with that new HTML. For example:
views.py
def index(request):
return render(request, "index.html")
def ajax_update(request):
return HttpResponse("<h1>Updated Header</h1>")
index.html
...
<div id="update_this_header">
<h1>Old header</h1>
</div>
<button id='updater'>
...
<script>
$("#updater").click(function() {
$.ajax({
url: #url to ajax_update view
success : function(data) {
$("#update_this_header").html(data)
},
failure : function(data) {
...
}
});
});
</script>
Now clicking the updater button should update the contents of the update_this_header div with the HTML returned in the HttpResponse from our ajax_update view (I admit I didn't test this, forgive me if there's a typo). Updating your search results works the same way; you just need to do more processing in your ajax view to respond with the correct HTML.
I hope this helps make things somewhat clearer; please let me know if I can (try to) explain anything more fully. The important takeaway here is that an ajax view will provide you with Some Data. It's up to you to make sure your template can take that data and properly display it.
I have view in django that add product to the cart( i use django-carton 1.2). That my code:
def add(request,product_id):
cart = Cart(request.session)
product = Product.objects.get(pk=product_id)
if product.quantity >=1:
cart.add(product, price=product.price)
product.quantity-=1
product.save()
return render (request,'shopping/show-cart.html')
else:
return HttpResponse("No product ")
After that view has worked a certain product add to the cart, cart with all products is showing. The problem: when in browser I make function "reload current page" it increase quantity of products in my cart. At the same my remove product view is working, but it only try delete the product when page reload from the function of browser
You should only do actions that modify data - like add and delete - on a POST request, not a GET. You need to create a form in your page, even if it just contains a single button, and check if request.method == 'POST' in the view before doing anything.
Either include a form tag within your html that POSTS information or you can use an Ajax request call.
<script type="text/javascript">
$(document).ready(function(){
$('#add_cart_button').click(function(e)
{
$.ajax({
url: 'xxxxxxx',
type: 'POST',
dataType: 'html',
data: {
'somevariable' : 'somevalue'
},
async: false,
success: function(data){
$('#target').html(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("Status: " + textStatus); alert("Error: " + errorThrown);
}
});
When you make the Ajax call, it sends whatever you have in your data dictionary to the specified url. From there, Django implements whatever function you want to process that data on the backend and returns the information back. The success function basically gets that information back and does whatever you want with it. In your case, you probably just want to re-render that chunk of HTML that displays the items in your cart.The target tag within the success function is where that chunk of HTML will be rendered, so include a target div tag in your html where you want it to be displayed.
You can access the data from the ajax request in your view by doing request.POST.get('key',somedefaultvalue) (if you want to have a default value if it can't find the dictionary or if it's empty) or just request.POST[key].