How to save data attributes in database? - python

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.

Related

Limiting voting per user django

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.

Return Stripe Session.id from Django view

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

Validate POST request (Vue + DRF)

So here's expected flow of the request:
The user creates a new Language via an html form. [√ ]
Vue via axios will fire a POST request to drf. [√ ]
Drf will validate the data from the POST request (see if the language name/id/etc already exists) [x]
Create the new language if it passes the validation. [x]
I'm currently stuck on #3.
I tried putting this on my LanguageViewSet:
def post(self, request):
language = request.data.get('language')
serializer = LanguageSerializer(data=language)
if serializer.is_valid(raise_exception=True):
language_saved = serializer.save()
return Response({"success": "Language '{}' created successfully!".format(language_saved.name)})
However, this doesn't somewhat work and gets completely ignored since:
I tried commenting the post function, but still if I call a POST request via axios on the LanguageViewSet it would still post. probably a built-in POST feature?
If the function is there, notice I used language = request.data.get('language') which means on my axios, the name of my data to be sent should be language right? otherwise it would ignore the POST request. I used created_lang in axios, fired the POST req but still it posted without any errors as if it completely ignored my post function.
If I tried posting a new language of which it's name is already registered on the database, it would still create it making duplicate records.
Forgive my naiveness I am completely new to drf and django :<
Here's my codes:
Language model:
class Language(models.Model):
name = models.CharField(max_length=100, default='New Language')
def __str__(self):
return self.name
Its serializer:
class LanguageSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Language
fields = ['id', 'name']
view:
class LanguageViewSet(viewsets.ModelViewSet):
queryset = Language.objects.all().order_by('name')
serializer_class = LanguageSerializer
def get_queryset(self):
queryset = Language.objects.all().order_by('name')
lang = self.request.query_params.get('lang','')
if lang:
return Language.objects.filter(pk=lang)
else:
return queryset
and url:
router = routers.DefaultRouter()
router.register(r'languages', views.LanguageViewSet)
On my frontend, here's my form:
<form #submit.prevent="PostLanguage" class="mt-3" action='' method="POST">
<input type="text" v-model="created_lang.name" name="name" id="name" placeholder="Language Name" autocomplete="off" required>
<input type="submit" value="Post">
</form>
And will be posted by this simple Vue script:
PostLanguage(){
let params = Object.assign({}, this.created_lang)
axios.post(
'http://127.0.0.1:8000/api/languages/', params
)
.then(response=>{
console.log(response)
this.GetLanguages()
this.created_lang.name = ''
})
.catch(error => {
console.log(error)
})
}
Update
I tried this:
class LanguageSerializer(serializers.ModelSerializer):
class Meta:
model = Language
fields = ['id', 'name']
def validate_name(self, value):
existed_language = Language.objects.filter(name=value).get()
if existed_language.name == value:
return Response(status=400)
else:
return value
if the name data from the POST is new (it's not used in the database) it would then return the value of it thus creating a new language. However if it already exists, I tried returning a response but it would create a language with its name = <Response status_code=400, "text/html; charset=utf-8">. I know it's kinda funny but I think this is a step to the right direction.
If language is unique in your model then add unique true in your model definition.
class Language(models.Model):
name = models.CharField(max_length=100, default='New Language', unique=True)
def __str__(self):
return self.name
This will cause serializer.validate to fail and won't create duplicate languages.
Update
The validate_name in your serializer returns the value of the filed after executing the validation logic. So you can update it with raise serializers.ValidationError("Language already exist") instead of Response (400) statement.

Django Search Bar Implementation

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.

Pulling Data from Django database

I am doing the basic Django polls tutorial and am placing the polls results into Kendo graphs. I Currently have a bar, donut, and bubble chart showing the results for the selected poll question. I want to add a line chart, but instead of using just the results from the selected poll, to also include data from the other questions. Is there a way of doing this, for I am unable to find the answer. I have placed my current code below.
# Javascript in results.html
$("#chart4").kendoChart({
legend: {
position: "bottom"
},
seriesDefaults: {
type: "line"
},
series: [
# Don't know what to do here
{
name: ?
data: ?
}
],
valueAxis: {
labels: {
format: "{0}%"
}
},
categoryAxis: {
categories: [{% for answer in question.answer_set.all %}"{{answer.choice_text}}", {% endfor %}]
}
});
views.py
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('id')
models.py
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('Date Published')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Answer(models.Model):
question = models.ForeignKey(Question)
choice_position = models.IntegerField(default=0)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
The easiest way for doing that is querying Django through Ajax. You must define a view that return some data (preferably json) and then call it from your js script.
from django.core import serializers
from django.http import HttpResponse
def all_questions(request):
"""
Most of the time you will have to do some extra work formating the json in
order to match the format that the JS library is expecting.
I can see your graph is expecting something like:
series: [{
name: <some_sustom_name>
data: <array of values>
}
],
So you need to provide to the JS library the data it is expecting.
That means some array (or perhaps similar data structure).
"""
questions = Questions.objects.all()
response = serializers.serialize('json', questions)
# Now response contain a representation of a
# json object that has a property called data
# besides, that property contain a list of Django objects.
response = "{data: %s}" % response
return HttpResponse(response, content_type="application/json")
For more info about the content of response see: Serializing Django Objects
Now, lets assume you got the data in your javascript (the Ajax call was successfull) you will have something like:
{data: [list of serialized Django objects]}
You just have to process list above and extract the data for your graph. Of course you can obtain that list directly from the Django view . That's your call.
See this Kendo demo for more info on what to put in series section of graphs.
Querying Django through Ajax from JQuery.
For this you need code like this:
$.ajax({
type: "GET", // 1
url:"url_to/you_view", // 2
data: { // 3
'zip': zip,
},
success: function(data){ // 4
},
error: function(error){ // 5
alert("Error");
}
});
1 - Specification of the request method (GET, POST, UPDATE, etc ....)
2 - The url pointing to your view.
Note that there are any protocol specification (http). As your js lives in your django app
the url is relative to this app. For this you need some url like:
urlpatterns = patterns('your_app.views',
...
url(r'url_to/you_view', 'your_view')
...
)
3 - Optional, some data to send to de django view. (in your case you don't need it)
4 - This is important, here you will process the data returned by the server when the request success.
Following the example above data here is a json object like:
{data: [...]}
where [...] stands for a list of django serialized objects with jason format.(See the links for documentation).
5 - On error this function will be called instead of that specified by success.
For more info on $.ajax object from JQuery see the $.ajax API Reference.

Categories

Resources