How to implement a Like button django - django

Please I am a newbie in programming,dont be pissed if my question is too rudimentary or annoying.I have a model that goes like this
class Feed(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,related_name='feeds')
text=models.TextField(blank=False,max_length=500)
user_like=models.ManyToManyField(User,related_name="likes",blank=True)
I want to create a stand-alone Like button for this model to add user to user_like when click by a user in template.I have a view like this
def like_feed(request,pk):
feed=get_object_or_404(Feed,pk=pk)
user=request.user
if request.method=='POST'
if feed.user_like.filter(id=user.id).exists():
feed.user_like.remove(user)
#make the change and reload the page
return render(request , 'index.html' ,{'feed':feed,})
else:
feed.user_like.add(user)
#make the change and reload the page
return render(request , 'index.html' ,{'feed':feed,})
urls.py
url(r'^likes/(?P<pk>\d+)/$',views.like_feed,name='like_feed_url')
and somewhere in the template I have
<form action="{% url 'like_feed' pk=feed.pk %}" method="post">
<input type="button" id="like" name="like_button" value="Like" /></form>
As I said I am not really sure what I am doing,I am just experimenting.Kindly help me out.

Related

CreateView form is not "rendering" in a template tag

Setup
I have successfully setup a CreateView class that allows user to create a Journal. You only have to enter the name and press the "Add" button.
views.py
...
class CreateToJournal(LoginRequiredMixin, CreateView):
model = to_journal
template_name = 'to_journals/to_journal_list.html'
fields = ('journal_name',)
def get_success_url(self):
return reverse('to-journals')
def form_valid(self, form):
form.instance.journal_user = self.request.user
return super(CreateToJournal, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(CreateToJournal, self).get_context_data(**kwargs)
context['to_journals'] = to_journal.objects.filter(journal_user=self.request.user)
return context
...
to_journal_list.html
...
<form class="" id="myForm" action="" method="post">
{% csrf_token %}
{{ form }}
<button type="submit" id="add-button">Add</button>
</form>
...
This creates a new Journal and reloads the page to reflect the changes made.
I am now trying to move the whole Journal creation process to the "home" page, using a template tag. Mostly it works fine and renders all user journals on the home page, but there is a problem with form rendering.
Problem
The new template tag does not render the form created by CreateView. The input field is not displayed here on the page with a template tag.
I couldn't find any specific information (or general, for that matter) regarding the relationship between forms and templatetags.
My setup for the template tag is the following:
...
├───pages
│ └───templatetags
│ └───__init__.py
│ └───journal_tags.py
...
├───templates
│ └───template_tags
│ └───journal_list.html
...
Where pages is the app for "static" pages.
journal_tags.py
from django import template
from to_journals.models import to_journal
register = template.Library()
#register.inclusion_tag('template_tags/journal_list.html', takes_context=True)
def get_journals(context):
journals = to_journal.objects.filter(journal_user=context['request'].user)
return {'journals': journals}
journal_list.html
...
<form class="" id="myForm" action="" method="post">
{% csrf_token %}
{{ form }}
<button type="submit" id="add-button">Add</button>
</form>
...
home.html
...
{% load journal_tags %}
{% get_journals %}
...
Thanks for taking the time to look at this question. Any help is appreciated.
If more information is needed I will happily provide.
Edit
I know realize that I have no reference to the view class created class CreateToJournal(LoginRequiredMixin, CreateView) in my template tag at all. I couldn't find any resource online that would help me figure it out. How can achieve this? I hope my explanations are celar. Thanks in advance.
Edit 2
Sorry for not being clear. Here is a little more in depth more clear explanation. Thanks.
Currently, my view class CreateToJournal(LoginRequiredMixin, CreateView) is rendered at http://127.0.0.1:8000/journals/ and looks like this:
Here you enter the name and press Add. Page reloads and the new journal is added to the list.
I want to achieve the same thing on http://127.0.0.1:8000/. I thought the best way to achieve this is with a templatetag. Below is the image of my current result. I am now able to pull all the journals on the home page, but the only thing that is not showing is the input field for the journal.
Ultimately, I want to have a few forms shoing on the home page in the future. I thought that templatetags is a good and sustainable way to do that.
Edit 3
I am now adding the actual view to the journal_tags.py
from django import template
from to_journals.models import to_journal
from to_journals.views import CreateToJournal
register = template.Library()
#register.inclusion_tag('template_tags/journal_list.html', takes_context=True)
def get_journals(context):
journals = to_journal.objects.filter(journal_user=context['request'].user)
return {'journals': journals,
'form': CreateToJournal(), #new
}
The output is not the actual form, rather the type of the object.

django, views direct to a another html page

I am using Django for develop a website. The website is intended to use to search information stored in a MySQL database.
This is the current basic flow of the web site.
1) index.html - this has a form to select an option
2) according the option, users will redirect to search.html (include a form)
3) once the user provides the criteria, the result will be displayed in reply.html
In my views.py , I have two functions.
from django.shortcuts import render
from website.models import WebsiteRepository
from .forms import SearchForm
from .forms import SelectTypeForm
def Search(request):
if request.method == 'POST':
#do something
return render(request, 'reply.html', {'env_dict':env_dict})
else:
#do something
return render(request, 'search.html', context = context)
def index(request):
if request.method =='POST':
#do something
return render(request, 'search.html', context = context)
else:
#do something
return render(request, 'index.html', context= context)
When I go to index.html page, I can select a option and it will direct me to search.html. After, I fill the form there and submit, it wont give me the reply.html page.
I have a feeling that, I could make this work by changing urls.py.
from django.urls import path
from website import views
urlpatterns = [
path('', views.index, name='index'),
#path('search/', view.Search, name ='Search')
]
I tried to google it. But its too much details and Iam kind of lost.
Do any of you guys know how to achieve this?
Thanks
search.html
{% extends "base_generic.html" %}
{% block content %}
<h3>Welcome to search information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
{% endblock %}
index.html
{% block content %}
<h3>Welcome to information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
just for clarify things more, ill add the forms.py too
from django import forms
from .models import WebsiteRepository
class SearchForm(forms.Form):
websiterepository = WebsiteRepository
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
class SelectTypeForm(forms.Form):
OPTIONS = (('1', 'Envirnmental Indicators'),('2','Economic Indicators'),('3','Social Indicators'),)
types = forms.ChoiceField(choices=OPTIONS)
Your code is wrong on many points.
First thing first: for a search, you want a GET request, not a POST (POST is for updating the server's state - adding or updating your database mostly). This is the semantically correct method (since you want to GET data), and it will allow a user to bookmark the url.
Second point: you don't want to submit the search form to the index view but to the search view. No need for redirects etc, just use the {% url %} templatetag to fill the action attribute of your form (you of course need to have a 'Search' url in your urls.py):
<form method="get" action="{% url 'Search' %}">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
if you want to have this form on more than one page (which is often the case for search forms), use an inclusion tag tha will take care of creating an unbound SearchForm and render the template fragment.
Then in your search view, you only want GET requests, and do not use two different templates, this will only lead to useless duplication.
def Search(request):
form = SearchForm(request.GET)
# use the form's data - if any - to get search results
# and put those results (even if empty) in you context
return render(request, 'reply.html', {'env_dict':env_dict})
And finally, your search form is totally broken:
class SearchForm(forms.Form):
# this is totally useless
websiterepository = WebsiteRepository
# this will only be evaluated once at process startup, so you will
# get stale data in production - and probably different data
# per process, in a totally unpredictable way.
# You need to either put this in the form's __init__ or wrap it
# in a callable and pass this callable
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
# are you going to manually add a new year choice every year ???
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
For the "indicators" ChoiceField you want something like:
def get_indicators_choices():
return Websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
class SearchForm(forms.Form):
# IMPORTANT : we are NOT calling the function here, just
# passing it (python functions are objects) to the field, which
# will call it everytime the form is instanciated, so you don't
# have stale data
indicator = forms.ChoiceField(
choices=get_indicator_choices,
label='Indicator')
As a last note: be consistent with your namings (ie why name one view in all lower (index) and capitalize the other (Search) ? Whichever convention you choose (I strongly suggest respecting pep8 here), at least stick to it for the whole project.
The problem is that code is not redirecting to /search, instead rendering search.html after post from index.html.
Try doing like-
views.py-
#your code
def index(request):
#do something
if request.method == 'POST':
return redirect('Search')
else:
#render index.html
def search(request):
#do something
if request.method == 'POST':
#render reply.html
else:
#render search.html
Another way to achieve this is if you specify action in your form so that form posts on /search.
search.html
<form method="post" action="/search">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>

Add value on IntegerField with a button(Django 1.11)

models.py
likes = models.IntegerField()
forms.py
class LikeForm(forms.ModelForm):
class Meta:
model = Post
fields = ("likes",)
How could I make a button that add 1 to this IntegerField every time it's clicked? The default value is 0.
I'm guessing I have to use "submit" button but I'm not sure how I can do that with out rendering the form on the page.
In your views.py you could add something like this:
def record_like_view(request, pk):
if request.method == 'POST':
post = Post.objects.get(pk=pk)
post.likes += 1
post.save()
...
Then in your template:
<form method="post">
{% csrf_token %}
<a class="btn" href="{% url 'my_app:record_like' post.id %}">Like</a>
</form>
You are just posting to a URL. Even though you are still using a form in your template, there is no need for the LikeForm in this case. Take a look at this Django tutorial for another example.
As far as a user preventing a user from clicking multiple times (as pointed out by guillermo) this would require something a little more complicated.

Proper way of using url patterns

I've created a form which by submit uploads an item to the database. The problem is that if I press f5 it'll submit the form again, because of the URL is now different.
I have these two url patterns
urlpatterns = [
url(r'(?i)^CMS/$', views.CMS, name='CMS'),
url(r'^createItem/$', views.createItem, name='createItem')
]
and my view looks like this
def CMS(request):
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
def createItem(request):
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
pass
form = itemCreateForm()
context = {
'form' : form,
'message' : 'ItemCreated!'
}
return render(request, 'CMS.html', context)
the CMS.html
{% if message %}
{{ message }}
{% endif %}
<div class='newItemFields'>
<form action="{% url 'kar:createItem' %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
my form
class itemCreateForm(ModelForm):
class Meta:
model = item
fields = ['name', 'type', 'price']
I start at homepage/CMS/ and fill in the form and press submit, and view function createItem runs and creates and saves the object in the database. And sends the user to homepage/CMS/createItem. And now everytime the user press f5 the createItem function will run again and insert another object into the database with the same values as the previous one, even though the input fields are empty (can't wrap my head around that).
I also twice write form = itemCreateForm() which I believe is dubious?
What I'd like to do is after createItem is run, it should send the user back to homepage/CMS/ and not homepage/CMS/createItem. Would that be the proper way to do it? Or is there a smart way of doing this.
At the end of your createItem function, you are rendering HTML of the page rather than redirecting. Instead, you need to do
return HttpResponseRedirect(reverse('kar:index'))
You will need to import HttpResponseRedirect and reverse which is used to resolve the URL through its name.
Check this out: https://docs.djangoproject.com/en/1.10/topics/forms/#the-view
What I'd like to do is after createItem is run, it should send the
user back to homepage/CMS/ and not homepage/CMS/createItem. Would that
be the proper way to do it? Or is there a smart way of doing this.
That would indeed be the proper and smart way to do it. Have one view handle both GET and POST and then redirect after successful form submission. This ensures that the user can't resubmit the form merely by refreshing. And you address your concern about repeating your code.
urlpatterns = [
url(r'(?i)^$', views.index, name='index'),
url(r'^createItem/$', views.createItem, name='createItem')
]
Then combine your views
def createItem(request):
if request.method == 'POST':
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
return HttpResponseRedirect('/homepage/CMS/')
else :
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
Note that the code is now shorter, it gives proper feedback to the user when the form is not valid. And you can't refresh to submit the for twice. We need a small change to the template
<div class='newItemFields'>
<form action=method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
The message display part isn't needed anymore

Add form returns None as record id, causing URL to error

I'm using the code found here (SO.com) to use the same template to both add and edit a record, but when I add a new record and click Submit, I get a 404 on the URL http://192.168.1.3:5678/app/student/edit/None/, and I'm not exactly sure why.
Here is the relevant portion of my urls.py:
url(r'^app/lesson/new/$', 'edit_lesson', {}, 'lesson_new'),
url(r'^app/lesson/edit/(?P<id>\d+)/$', 'edit_lesson', {}, 'lesson_edit'),
Here is the relevant portion of my views.py:
def edit_lesson(request, id=None, template_name='lesson_edit_template.html'):
if id:
t = "Edit"
lesson = get_object_or_404(Lesson, pk=id)
stu = get_object_or_404(Student, pk=sid)
if stu.teacher != request.user:
raise HttpResponseForbidden()
else:
t = "Add"
lesson = Lesson()
if request.POST:
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
# If the save was successful, redirect to another page
return view_lessons(request)
else:
form = LessonForm(instance=lesson)
return render_to_response(template_name, {
'form': form,
't': t,
'lesson': lesson,
}, context_instance=RequestContext(request))
And finally, here is my template:
<h1>{{ t }} Lesson</h1>
<form action="/app/lesson/edit/{{ lesson.id }}/" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I'm certain that I'm missing something really easy, but I can't seem to put my finger on it. I'm using Django 1.3.1 if that makes any difference.
Thanks,
MC
There's no need to specify any URL in the form's action attribute. Just do
<form action="" method="post">
and it will POST back to the URL that you originally used to access it, which is what you want.
In add case {{ lesson.id }} is None, because lesson is unsaved Lesson() instance, without pk, so your form is being fired to nonexistent URL.
I recommend separating create and edit views and processing them in different ways (or even inherit generic views - with new class-based generic views it's easy and pleasant).
Also, use {% url %} template tag everywhere instead of hard-coded urls.