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.
Related
I'm working on a social network. I want to load the comments of each post so I make an API call to the server to fetch all the comments of the required posts. The code will make everything clear:
urls.py
path("comments/<int:post_id>", views.load_comments)
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
commented_by = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.CharField(max_length=128)
views.py
def load_comments(request, post_id):
"""Returns the comments to index.js"""
try:
# Filter comments returned based on post id
post = Post.objects.get(pk=post_id)
comments = list(Comment.objects.filter(post=post).values())
return JsonResponse({"comments": comments})
except:
return JsonResponse({"error": "Post not found", "status": 404})
index.js
fetch(`/comments/${post_id}`)
.then(res => res.json())
.then(res => {
// Appoints the number of comments in the Modal title
document.querySelector("#exampleModalLongTitle").textContent = `${res.comments.length} Comments`;
res.comments.forEach(comment => {
modal_body = document.querySelector(".modal-body")
b = document.createElement("b");
span = document.createElement("span");
br = document.createElement("br");
span.classList.add("gray")
b.textContent = comment.commented_by_id + " ";
span.textContent = comment.comment;
modal_body.appendChild(b);
modal_body.appendChild(span);
modal_body.appendChild(br);
})
I can get the comment value using comment.comment in js. However, the problem arises when I convert the comments objects to list.values so now I lose the ability to get the user who posted the comment (commented_by)
Any help to get the comment and commented_by values is appreciated.
I also tried in views.py:
def load_comments(request, post_id):
"""Returns the comments to index.js"""
try:
# Filter comments returned based on post id
post = Post.objects.get(pk=post_id)
post_comments = Comment.objects.filter(post=post)
comments = []
for comment in post_comments:
comments.append({
"comment": comment.comment,
"commented_by": commented_by,
})
comments = serializers.serialize('json', comments)
return HttpResponse(comments, content_type='application/json')
except:
return JsonResponse({"error": "Post not found", "status": 404})
However when I try this, it outputs the error.
In Django, the values() function for a ForeignKey field by default returns the id of the related object with the key formed by appending the name of the field with "_id". So in your case, you have your user ID under the key commented_by_id.
If by "loosing the ability to get the user who posted the comment" you mean other user info, like username, then you can pass the fields that you need to the values() function.
comments = list(Comment.objects
.filter(post=post)
.values("comment",
"commented_by__username"
))
will give a dict with the comment text and the user name (assuming that you have a username field in your User model.
1- you should use django_Rest_framework cuz it's very easy to work with api and show related field
2- use Post.objects.get(id=post_id) instead of pk
or you should skip this step by filter the comments depend on post id directly likeComment.objects.all().filter(post_id=post_id)
I have a problem I am currently working on. Really new to django and python. What I am trying to do is I have a form where the user can rent a parking lots for a certain period of time.
I want to be able to dynamically calculate the value of the price based on the time the user selects and show this to the user. Meaning that price x time selected will generate a price that the user should be able to see.
I do have a view where the user can rent a parking lot. I tried to write a function where the price is dynamically calculated, but I can't come up with how to get the user data before the reservation form is submitted.
Below you see the view I wrote for the reservation:
def list_parking_lots(request):
"""
View for all parking lots
:param request: request from user
:return: rendered parking_mgmt/list_parking_lots.html
"""
parking_lots = ParkingLot.objects.all()
form = ReservationForm()
if request.method == 'POST':
form = ReservationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Your reservation was successfully registered.')
form = ReservationForm()
return render(request, 'parking_mgmt/list_parking_lots.html', {'parking_lots': parking_lots, 'form': form})
OK. So thanks to you guys input I have created a simple ajax request. Now I am still not sure if I understand it Right tho. With the ajax url parameter I should be able to send the data I want to the view above, right?
This is the ajax request:
$.ajax({
type: "POST",
data: $("#reservationModal").serialize(),
url: "{% url 'parking_lots' %}",
success: function(data)
{
//alert(data);
$('#userError').html(data);
}
});
Any help is appreciated.
I have a mini project with Django.
I have just a model with a counter total :
class Reponse(models.Model):
total = models.IntegerField()
I have this view:
def questions(request):
_reponse = get_object_or_404(Reponse, id= 1)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
And my HTML test display the variable total:
<p id="block1">
{{reponse.total}}
</p>
My question is :
I would like when I click on a button in the HTML, the variable total increases by 1.
How can I do that ? With a form POST or GET ? May be there is a better method ?
A button like this :
<p id="block1">
{{reponse.total}}
</p>
<button type="button">
Click for increase total by 1
</button>
You can do that way
from django.db.models import F
def questions(request)
_reponse = Reponse.objects.update(total=F('total') + 1)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
If you want to add a button to increase the counter so you need to create two separate views one to render the html page and the other to increase the integerfield
So you views.py will look like this
from django.db.models import F
from django.views.decorators.csrf import csrf_exempt
def questions(request)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
#csrf_exempt
def IncreaseCounter(request):
_reponse = Reponse.objects.update(total=F('total') + 1)
return HttpResponse('the counter has been increased')
and in your url you will have :
path('question_page/', views.questions, name='question-html'),
path('increase_counter/', views.IncreaseCounter, name='Increase-Counter')
And last you need just to add a button to target the second view :
<button onclick="window.location.href='/increase_counter/';"> + 1 </button>
And the ideal way is to use ajax so your page will not refresh every time you click the button, to do so you have to change the onclick function of the button and to add the following script :
<button onclick="increase_counter()"> + 1 </button>
<script type="text/javascript">
$.ajax({
url: '/increase_counter/',
method : 'POST',
success: function(response) {
alert('counter increased')
}
});
</script>
But if you want to use ajax you have to add a csrf_exempt decorator on your view.
In order to update a specific object in your model you need to pass the pk as a variable in your url like so :
path('increase_counter/<int:pk>/', views.IncreaseCounter, name='Increase-Counter')
in your button you will loop change the button to be like this :
<button onclick="window.location.href='/increase_counter/{{ response.pk }}/';"> + 1 </button>
for aajax is the same method you add the pk into the url.
And in your view you will add this :
def IncreaseCounter(request, pk):
_reponse = Reponse.objects.filter(pk=pk).update(total=F('total') + 1)
return HttpResponse('the counter has been increased')
In my opinion, this is a much easier straightforward method. This isn't exact code for your problem, but more of a general flow on what the heck all this stuff is and how to use it. So apologies on the wordiness of this post. I am posting for my case because I think will be useful to you as you expand your app to needing to control totals for a particular object. So, in my case, I was needing a button to easily increase or decrease a part quantity for an inventory system, like nuts and bolts. This is what I needed to control inventory while on a part-specific page:
A button with a reverse pattern name that links to increase_inventory view.
Add 1</button>
Notice the "parts.pk", I'm going to talk about it in sec. Also, now and later notice the differences between _ and -. When you see -, it's a pattern name.
In your urls.py, place a similar code like this in your file.
urlpatterns = [
path('increase_inventory_level/<pk>', views.increase_inventory, name='increase-inventory'), ]
Notice a few things here too,
"inventory-level" is the pattern name which is the linking variable between your html page and urls.py.
"increase_inventory_level/<pk" is the URL link for a specific object. Say in your parts list you have: a nut, a bolt, and an o-ring. Each one of these objects is going to have a different PK. Each one of these items has it's own detail page which is going to have its own url, which will look something like this partdetail/<pk. (Close those brackets for pk, I can't in this post or it will disappear)
The last thing to notice is the "views.increase_inventory". This links your app to the specific view that you need.
def increase_inventory(request, *args, **kwargs):
pk = kwargs.get('pk')
part = get_object_or_404(Part, pk=pk)
part.inventory_level = part.inventory_level + 1
part.save()
context = {'part': part}
return render(
request,
'webapp/part_detail.html',
context=context
)
Now we're cooking. The view grabs the object associated with the PK value then edits the inventory attribute of the part object, saves it, and then renders the html page with the new part information.
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 need django modelform with 2 fields, where second field choice list depends on what was chosen in first one. My model:
class Offer(BaseModel):
VEHICLE_TYPES = (
('personal','Personal car'),
('truck','Truck'),
)
vehicle_type = models.CharField(max_length=32, choices=VEHICLE_TYPES, default='personal', verbose_name='Vehicle type')
PERSONAL_MAKES = (
('',''),
)
TRUCK_MAKES = (
('',''),
)
make = models.CharField(max_length=32)#what more??
How can I set choices of make field to PERSONAL_MAKES if vehicle_type is set to personal? How can I do this? Is it possible on model level?
You probably can't because it depends of user interaction with your form: your server can't know in advance which element your user will select before sending the form to the browser. You could probably achieve this using ajax. I think a working process could be :
Create a form with all the fields, and make make field hidden
Create a view (I'll call it AjaxMakeFieldView) that will catch an ajax request taking a vehicle_type argument and return the HTML for make field, populated with relevant data. Add a URL in your URLConf for this view.
In your template, add a Javascript binding : when user select a vehicle_type, the browser will send aan ajax request to AjaxMakeFieldView and replace hidden make field with returned HTML
If you don't want javascript, another way would be a two step form :
A first form with a vehicle_type field
Once the first form is submitted, your user get a second form with a make field, which initial data is populated depending of vehicle_type selected in the first form.
I've never done this, but Django documentation on Form wizard seems a good place to start.
This is how I ended up having two model choice fields depending on each other, on one page. In the below, field2 is depending on field1:
Javascript portion
Note that in $.each(), $.parseJSON(resp) should NOT be used (instead of just json) as we're already have it parsed by jQuery (due to the content_type='application/json' response) - see I keep getting "Uncaught SyntaxError: Unexpected token o".
$(document).ready(function() {
$("#id_field2").empty();
$("#id_field1").change(function(){
$.ajax({
url: "{% url 'ajax_get_field_2' %}",
type: 'GET',
data: {field1_id: $("#id_field1").val()},
dataType: "json",
success: function(resp){
$("#id_field2").empty();
$.each(resp, function(idx, obj) {
$('#id_field2').append($('<option></option>').attr('value', obj.pk).text(obj.fields.code + ' (' + obj.fields.affection + ')'));
});
},
error: function(jqXHR, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
});
Django views.py portion
Note that this can probably be done by django-rest-framework as well.
I'm obtaining fields=('id', 'code', 'affection')) from my MyModel2 - these can then be reached in JQuery using obj.fielsd.<myfieldname>.
class AjaxField2View(generic.View):
def get(self, request, *args, **kwargs):
field_1 = get_object_or_404(MyModel1, pk=request.GET.get('field1_id', ''))
model2_results = MyModel2.objects.filter(k_value=field_1 .k_value)
return HttpResponse(serializers.serialize('json', model2_results, fields=('id', 'code', 'affection')), content_type='application/json')
Another smart method query/free and no views needed,
is to make html tags for options that related to field A choice.
For example, in field B options:
<option example_tag="example_value" value="5"></option>
This tag should be related to either field A value or text.
Using JS to display block/none using this option tag accordingly.
And don't forget to reset value for every time user changes field A.