How to implement Like Dislike button for anonymous user in Django? - python

How can I implement like and dislike button functionality for anonymous users in my django website?
I am recently developing django website,and decided not to use User Login & Logout for the purpose of more visiting users.
The problem is how to do implement IP Address instead of "request.user".
Anyone knows how to set IP address as if like a user for "like and dislike button"???
Below is my code so far (set request.user for like dislike function):
#views.py Dislike is the same code.
#login_required(login_url="login")
def Like(request, slug):
add = Add.objects.get(slug=slug)
# remove dislike when user hit like.
is_dislike = False
for dislike in add.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if is_dislike:
add.dislikes.remove(request.user)
# add like
is_like = False
for like in add.likes.all():
if like == request.user:
is_like = True
break
if not is_like:
add.likes.add(request.user)
if is_like:
add.likes.remove(request.user)
next = request.POST.get("next", "/")
return HttpResponseRedirect(next)
#review.html inside templates
<div class="rate-added-rule">
<form action="{% url 'like' added_data.slug %}" method="POST">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="like-button">
<i class="far fa-thumbs-up">LIKE<span>VOTES: {{ added_data.likes.all.count }}</span></i>
</button>
</form>
<form action="{% url 'dislike' added_data.slug %}" method="POST">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="dislike-button">
<i class="far fa-thumbs-down">DISLIKE<span>VOTES: {{ added_data.dislikes.all.count }}</span></i>
</button>
</form>
</div>

Related

django form not posting, but other form on page does?

I have two forms on a page. The first form properly sends a POST request to my view but the second form sends a GET (I need it to also send a POST). I've made this mistake before and this time I made sure that the button is set to "submit", but for some reason it still sends a GET.
form 1 (working as intended):
<form method="POST">
{% csrf_token %}
<div class="row form-row">
<div class="col-sm-6">
<div class="form-group">
<label for="start_date">Start</label>
<input class="form-control" type="text" data-flatpickr data-alt-input="true" data-date-format="Y-m-d" name="start_date" data-enable-time="true">
</div>
<div class="form-group">
<label for="end_date">End</label>
<input class="form-control" type="text" data-flatpickr data-alt-input="true" data-date-format="Y-m-d" name="end_date" data-enable-time="true">
</div>
</div>
</div>
<input type="hidden" name="eventtype" value="availability">
<button class="btn btn-primary" type="submit">Save changes</button>
</form>
form 2 (sends a GET but I want this to POST):
<form method="POST">
{% csrf_token %}
{% if form.errors %}{{form.errors}}{% endif %}
<input type="hidden" name="pk" value="{{ i.id }}">
<input type="hidden" name="eventtype" value="invite">
<button class="btn btn-primary" type="submit">Confirm</button>
</form>
views.py
def createevent(request):
if request.method == 'GET':
<<code>>
else:
try:
eventtype = request.POST.get('eventtype', None)
print(eventtype)
if eventtype == "availability":
form = EventForm(request.POST)
newEvent = form.save(commit=False)
newEvent.mentor_user = request.user
newEvent.requester_user = None
newEvent.notes = None
newEvent.save()
elif eventtype == "invite":
form = EventForm(request.POST)
updatedEvent = form.save(commit=False)
updatedEvent.isConfirmed = True
updatedEvent.save()
return redirect('createevent')
except ValueError:
print(form.errors)
return render(request, 'events/createevent.html', {'form':EventForm(), 'error':'There was an error. Please make sure you entered everything correctly!'})
urls.py
# Events
path('events/create', views.createevent, name='createevent'),
# path('calendar/<slug:slug>', views.viewevent, name='viewevent'),
path('events/<int:pk>/edit', views.updateevent, name='updateevent'),
console output for form 1 and form 2:
"GET /events/create?csrfmiddlewaretoken=<<redacted>>&pk=2&eventtype=invite HTTP/1.1" 200 25560
"POST /events/create?csrfmiddlewaretoken=<<redacted>>&pk=2&eventtype=invite HTTP/1.1" 302 0
Set the action of form to your url path.
Look at this:
<form name="form" method="POST" action="{% url 'your_url_name' %}">
The issue ended up being the form was wrapped in another form tag further up in the template code. When I clicked the Submit button, the Submit button was firing on the first form and not the second. Just a bad copy/paste.

Django request.POST.get() returns None

I face a problem and I can't find a solution.
I'm trying to redirect to the previous page after login. Somehow the ?next=request.path returns none when trying to request.POST.get() after submission.
This is my Html code that directs the user to the login page, taking the request.path as "next page after login" value.
{% if user.is_authenticated %}
<button class="btn" data-toggle="modal" data-target="#inquiryModal">
<a class="btn btn-primary border rounded-0"
role="button" href="#">Make an Inquiry</a>
</button>
{% else %}
<a class="btn btn-primary border rounded-0" role="button"
href="{% url 'login' %}?next={{ request.path|urlencode }}"
>Make An Inquiry</a>
{% endif %}
This is the login page html code.
<div class="login-clean" style="background-color: #fff;">
<form action="{% url 'login' %}" method="POST">
{% csrf_token %}
<!--- ALERTS -->
{% include 'partials/_alerts.html' %}
<div class="form-group">
<input class="form-control" type="email" name="email" placeholder="Email"></div>
<div class="form-group">
<input class="form-control" type="password" name="password" placeholder="Password">
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit">Log In</button>
</div>
</form>
</div>
Views.py file
def login(request):
if request.method == 'POST':
email = request.POST['email']
password = request.POST['password']
valuenext = request.POST.get('next')
print(valuenext)
user = auth.authenticate(username=email, password=password)
# if user is found and not from listing page login and redirect to dashboard
if user is not None and valuenext == "":
auth.login(request, user)
messages.success(request, 'You are now succesfully logged in')
return redirect('dash_inquiries')
# if user is found and from specific listing page login and redirect to the listing
elif user is not None and valuenext != "":
auth.login(request, user)
print("success")
messages.success(request, 'You are now logged in')
return redirect(valuenext)
else:
messages.error(request, 'Invalid credentials')
return redirect('login')
else:
return render(request, 'accounts/login.html')
What am I doing wrong here? The next value is passed in the url when directing to the login page, but I don't seem to correctly get() the next value in my backend as it keeps returning None.
Thanks in advance.
Clicking on following button will send a GET request.
<a class="btn btn-primary border rounded-0" role="button"
href="{% url 'login' %}?next={{ request.path|urlencode }}">Make An Inquiry</a>
This get request will render accounts/login.html template.
You're parsing request.POST.get('next') for POST requests only. But there is no next in
<form action="{% url 'login' %}" method="POST">
You need your form tag to look like
<form action="{% url 'login' %}next={{ next }}" method="POST">
To solve above issue, you need to parse 'next' for request.GET, and add it to context for response.
if request.method == 'POST':
# handle POST
else:
next = request.GET.get('next', '')
context = {'next': next}
return render(request, 'accounts/login.html', context=context)
And then, add this next into form.action.
<form action="{% url 'login' %}next={{ next }}" method="POST">
Okay, so I didn't make sure I passed the next value into the login form, therefore the solution was to add a hidden input to get the next value in the request:
<input type="hidden" name="next" value="{{ request.GET.next }}" />

Django - Submitting form with multiple inputs for same field

Forewarning: I'm very new to Django (and web development, in general).
I'm using Django to host a web-based UI that will take user input from a short survey, feed it through some analyses that I've developed in Python, and then present the visual output of these analyses in the UI.
My survey consists of 10 questions asking a user how much they agree with a a specific topic.
Example of UI for survey:
Example of UI input screen
For models.py, I have 2 fields: Question & Choice
class Question(models.Model):
question_text = models.CharField(max_length=200)
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
I am wanting to have a user select their response to all 10 questions, and then click submit to submit all responses at once, but I'm having trouble with how that would be handled in Django.
Here is the html form that I'm using, but this code snippet places a "submit" button after each question, and only allows for a single submission at a time.
NOTE: The code below is creating a question-specific form for each iteration.
{% for question in latest_question_list %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<div class="row">
<div class="col-topic">
<label>{{ question.question_text }}</label>
</div>
{% for choice in question.choice_set.all %}
<div class="col-select">
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
</div>
{% endfor %}
</div>
<input type="submit" value="Vote" />
</form>
{% endfor %}
I'm interested in how I would take multiple inputs (all for Question/Choice) in a single submission and return that back to views.py
EDIT: ADDING VIEWS.PY
Currently, my views.py script handles a single question/choice pair. Once I figure out how to allow users to submit the form one time for all 10 question/choices, I will need to reflect this in views.py. This could sort of be part 2 of the question. First, how do I enable a user to submit all of their responses to all 10 questions with one "submit" button? Second, how do I setup views.py to accept more than 1 value at a time?
views.py
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/survey.html', {
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:analysis'))
Please let me know if additional context it needed.
Thanks in advance!
-C
Use getlist()
In your views.py
if method=="POST":
choices = request.POST.getlist('choice')
I feel you should change the input radio to checkbox. Radio won't allow multiple selection but Checkbox will.
Refer here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.QueryDict.getlist
Ideally, this should have been done with Django Forms. Django forms have widgets and RadioSelect is one of them. You can use that to render your form and get the answer to each question at once.
But that will need a lot of change in the way you are currently doing things.
So, what you can do is, on click on a submit button, collect all the question/choice pairs and send them at once with a POST request.
{% for question in latest_question_list %}
<form>
<div class="row">
<div class="col-topic">
<label>{{ question.question_text }}</label>
</div>
{% for choice in question.choice_set.all %}
<div class="col-select">
<input type="radio" name="choice" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
</div>
{% endfor %}
</div>
</form>
{% endfor %}
<input id="submit-btn" type="submit" value="Vote" />
<script>
$(document).on('click', '#submit-btn', function(event){
var response_data = []
var question_objs = $('.col-topic');
var choice_objs = $('.col-select');
for(i=0;i<question_objs.length;i++){
var question_text = $(question_objs[i]).find('label').text();
var choice_id = $(choice_objs[i]).find('input').val();
var choice_text = $(choice_objs[i]).find('label').text();
var question_choice = {
"question_text": question_text,
"choice_id": choice_id,
"choice_text": choice_text
}
response_data.push(question_choice);
}
$.ajax({
type: "POST",
url: "url_to_your_view",
data: response_data,
success: function(response){
alert("Success");
}
});
});
</script>
This is how your view should look like.
def question_choice_view(request):
if request.method == "POST":
question_choice_data = request.POST['data']
# further logic
Now, question_choice_data is a list of dictionaries. Each dict in the list will have the question_text, choice_text and choice id of user's response.
You just need to organize your template a bit differently in order to have multiple questions within the same form. Litteraly in HTML it would translate into multiple text inputs and then one submit input below, all within one single form:
<form action="{% url 'polls:vote' question.id %}" method="post">
{% for question in latest_question_list %}
{% csrf_token %}
<div class="row">
<div class="col-topic">
<label>{{ question.question_text }}</label>
</div>
{% for choice in question.choice_set.all %}
<div class="col-select">
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
</div>
{% endfor %}
</div>
{% endfor %}
<input type="submit" value="Vote" />
</form>
Is it working now ?

Django - How to delete an article?

I'm trying to create a button on the framework django that let me delete my own article on my blog. I tried to create a code for this functionality, but it doesn't work.
views.py
if(request.GET.get('delete')): #if the button is clicked
delete_article = get_object_or_404(Article, id=id)
if request.POST:
form = DeleteNewForm(request.POST, request.FILES)
if form.is_valid():
delete_article.delete()
return HttpResponseRedirect("/")
template.html
<form enctype='multipart/form-data' action="." method="post" class="form" autocomplete="off" autocorrect="off">
{% csrf_token %}
<div class="form-group">TITRE
{{ form.title.errors }}
{{ form.title }}
</div>
<div class="form-group">CONTENU
{{ form.media }}
{{ form.contenu.errors }}
{{ form.contenu }}
</div>
<div class="form-group">
{{ form.image.errors }}
{{ form.image }}
</div>
<input type="submit" class="btn btn-default" value="Edit Article"/>
<input type="submit" class="btn btn-default" value="Delete Article" name="delete">
</form>
Here is what happen when I submit the form : I am not redirected on the index as it should redirect me and the article has not been deleted.
Is there a problem which don't let me delete my article in these lines?
I have no idea what you're doing in your views or in your template. But if I want to delete something, I just define a separate view for that.
# views.py
from django.views.generic.base import View
class DeleteView(View):
def post(self, request *args, **kwargs):
article = get_object_or_404(id=request.POST['article_id'])
# perform some validation,
# like can this user delete this article or not, etc.
# if validation is successful, delete the article
article.delete()
return HttpResponseRedirect('/')
Your template should be like this:
<form action="/url/to/delete/view/" method="POST">
{% csrf_token %}
<input type="hidden" value="{{ article.id }}">
<input type="submit" value="Delete">
</form>

Simple django POST not working

I have a form like this:
<form action="{% url "forum.posts" forum=forum.slug thread=thread.slug %}" method="POST">
{% csrf_token %}
<div class="form-group">
<textarea class="form-control" placeholder="Začni tipkati.."></textarea>
</div>
<input type="submit" class="btn btn-success" value="Pošlji odgovor">
</form>
views.py
'''
Display all posts in a thread or create a new post in a thread.
'''
def posts(request, forum, thread):
forum = Forum.objects.get(slug=forum)
thread = Thread.objects.get(slug=thread)
if request.method == "POST":
return HttpResponse('ok')
posts = thread.posts.all()
return render(request, 'forum/posts.html', {
'forum': forum,
'thread': thread,
'posts': posts
})
urls.py (relevant part):
# List all posts in a thread / Submit a post to a forum
url(r'^(?P<forum>[-\w]+)/(?P<thread>[-\w]+)/$', 'forum.views.posts', name='forum.posts'),
HTML output:
<form action="/forum/o-spletiscu/novatemaa/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="4PQWDsAfHyjrhUnYU5P9vVhtaY3vLPBU">
<div class="form-group">
<textarea name="post-body" class="form-control" placeholder="Začni tipkati.."></textarea>
</div>
<input type="submit" class="btn btn-success" value="Pošlji odgovor">
</form>
After I hit submit, I expect ok to be returned, instead page refreshes and nothing happens.
Normal GET requests are working though..
What am I missing?
Edit:
It's working when I use #csrf_exempt on posts method.
page refreshes and nothing happens
Problem with your action value. try to inspect it and find wither the url is correct or not.
OR
your form does not have any input with name property.
Try this:
<form action="{% url "forum.posts" forum=forum.slug thread=thread.slug %}" method="POST">
{% csrf_token %}
<div class="form-group">
<textarea class="form-control" placeholder="Začni tipkati.." name="something"></textarea>
</div>
<input type="submit" class="btn btn-success" value="Pošlji odgovor">
</form>

Categories