I have a Like feature, which works fine for the "Detailed Product". However, I want to add this feature on the main page, where multiple products are shown. Not sure, how to correctly do that.
urls.py:
url(r'^like$', views.like_product, name='like_product')
script in the base.html:
<script type="text/javascript">
$(document).ready(function(event){
$(document).on('click', '#like', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: '{% url 'like_product' %}',
data: {'id': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error: function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>
likes.html:
<form action="{% url 'like_product' %}" method="post">
{% csrf_token %}
{% if is_liked %}
<button type="submit" id="like" name="product_id" value="{{ product.id }}" class="btn btn-danger">Dislike</button>
{% else %}
<button type="submit" id="like" name="product_id" value="{{ product.id }}" class="btn btn-primary">Like</button>
{% endif %}
</form>
views.py:
def home(request):
products = Product.objects.all().order_by('-pub_date')
f = ProductFilter(request.GET, queryset=products)
context = {
'filter': f,
}
return render(request, 'product/home.html', context).
def detail(request, product_id):
product = get_object_or_404(Product, product_id=product_id)
is_liked = False
if product.likes.filter(id=request.user.id).exists():
is_liked = True
context = {
'product': product,
'is_liked': is_liked,
'total_likes': product.total_likes()
}
return render(request, 'product/detail.html', context)
def like_product(request):
product = get_object_or_404(Product, id=request.POST.get('id'))
is_liked = False
if product.likes.filter(id=request.user.id).exists():
product.likes.remove(request.user)
is_liked = False
else:
product.likes.add(request.user)
is_liked = True
context = {
'product': product,
'is_liked': is_liked,
'total_likes': product.total_likes()
}
if request.is_ajax():
html = render_to_string('product/likes.html', context, request=request)
return JsonResponse({'form': html})
Likes/Dislikes are being recorded correctly if clicked from the main page, however they are not correctly displayed (actual amount of likes and Dislike button can only be seen on the "Detailed Product" page). I suspect this is because I have id="like" in the likes.html for both buttons. I suspect the jQuery script needs to be changed as well. Not sure how to do that. Thanks in advance for your help.
I assume you are getting ID from URL to details page. It really depends how you are generating the list of products on the page but if you are using loop in templates then just add like button under each product and then use it's id to set up like form.
Related
I am trying to build a dependant dropdown in a django form, but it is not working. I have followed videos and tutorials, but got no luck.
I would like to select a brand of a car (make) and then a model of a car. The model depends on the car's brand, of course.
I have followed this tutorial https://python.plainenglish.io/python-and-django-create-a-dependent-chained-dropdown-select-list-b2c796f5a11
Status: The "Make" dropdown works fine. The "Model" dropdown is never showing anything.
It just does not work, but no error is shown... :S
models.py
from django.db import models
from django import forms
class Vehicle(models.Model):
make = forms.CharField(max_length=30)
model = forms.CharField(max_length=30)
...omitted
forms.py
from django import forms
from .models import Vehicle
import json
def readJson(filename):
with open(filename, 'r') as fp:
return json.load(fp)
def get_make():
""" GET MAKE SELECTION """
filepath = '/Users/alvarolozanoalonso/desktop/project_tfm/tfm/JSON/make_model_A.json'
all_data = readJson(filepath)
all_makes = [('-----', '---Select a Make---')]
for x in all_data:
if (x['make_name'], x['make_name']) in all_makes:
continue
else:
y = (x['make_name'], x['make_name'])
all_makes.append(y)
# here I have also tried "all_makes.append(x['make_name'])
return all_makes
class VehicleForm(forms.ModelForm):
make = forms.ChoiceField(
choices = get_make(),
required = False,
label='Make:',
widget=forms.Select(attrs={'class': 'form-control', 'id': 'id_make'}),
)
...omitted
class Meta:
model = Vehicle
fields = ['make', 'is_new', 'body_type', 'fuel_type', 'exterior_color', 'transmission', 'wheel_system', 'engine_type',
'horsepower', 'engine_displacement', 'mileage', 'transmission_display', 'year', 'fuel_tank_volume',
'city_fuel_economy', 'highway_fuel_economy', 'maximum_seating']
model.HTML
{% block javascript %}
<script>
$("#id_make").change(function () {
var makeId = $(this).val();
$.ajax({
type: "POST",
url: "{% url 'get-model' %}",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'make': makeId,
},
success: function (data) {
console.log(data.models);
let html_data = '<option value="">-------</option>';
data.models.forEach(function (data) {
html_data += `<option value="${data}">${data}</option>`
});
$("#id_model").html(html_data);
}
});
});
</script>
{% endblock javascript %}
<form class="" action="" method="post">
{% csrf_token %}
{% for error in errors %}
<div class="alert alert-danger mb-4" role="alert">
<strong>{{ error }}</strong>
</div>
{% endfor %}
<div class="form-row">
<div class="form-group col-md-6">
<label>Status:</label>
{{ form.make }}
</div>
<div class="form-group col-lg-6">
<label >Model:</label>
<select id="id_model" class="form-control" name="state">
<option value="-----">Select Model</option>
</select>
</div>
...omitted
<div class="form-group col-md-6">
<button type="submit" class="btn btn-primary">Calculate</button>
</div>
</form>
views.py
def model(request):
context = {}
if request.method == 'GET':
form = VehicleForm()
context['form'] = form
return render(request, 'model.html', context)
if request.method == 'POST':
form = VehicleForm(request.POST)
if form.is_valid():
return render(request, 'model.html', context)
def getModel(request):
make = request.POST.get('make')
models = return_model_by_make(make)
return JsonResponse({'models': models})
Your change handler for id_make is attached before the select is created in the DOM, so your event handler does not fire.
You can use event delegation to set up an event handler before an element is created
$(document).on('change', "#id_make", function () {
var makeId = $(this).val();
$.ajax({
type: "POST",
url: "{% url 'get-model' %}",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'make': makeId,
},
success: function (data) {
console.log(data.models);
let html_data = '<option value="">-------</option>';
data.models.forEach(function (data) {
html_data += `<option value="${data}">${data}</option>`
});
$("#id_model").html(html_data);
}
});
});
What i want to do is adding photo in user album.
I have gallery.html (template of ListView).
It shows every photo in photo model.
Each photo has button for adding photo to user album.
click button -> shows form in modal -> select album -> submit
then photo is added to album, with no refresh.
It works... harf ;( photo is added to the album. it's nice.
But i get 405 error page. what am i missing??
my code:
form in gallery.html
<form class='addphotoform' method="POST" id='addphotoform'>
{% csrf_token %}
select album.<br>
<select class="form-select" id='album_pk' name='album_pk' aria-label="Default select example">
{% for album in user.albums.all %}
<option value='{{album.pk}}'>{{album.album_name}}</option>
{% endfor%}
</select>
<input type="hidden" name="photo_id" id="photo_id" value="{{photo.id}}"/>
<button type="button submit" class="btn btn-danger">add</button>
</form>
ajax
$('#addphotoform').on('submit', function(event){
$.ajax({
type: "POST",
url: "{% url 'album:add_photo' %}",
data: { csrfmiddlewaretoken: '{{ csrf_token }}',
photo_id : $('#photo_id').val(),
album_pk : $('#album_pk').val()
},
success: function () {
console.log('added');
},
error: function () {
console.log('failed');
},
});
})
album/view.py
#login_required
def add_photo(request):
if request.method == 'POST':
photo_pk = request.POST.get('photo_id')
album_pk = request.POST.get('album_pk')
photo = get_object_or_404(Photo, pk=photo_pk)
album = get_object_or_404(UserAlbum, pk=album_pk)
album.album_photos.add(photo)
return HttpResponse('add done')
else:
return HttpResponse('wrong approach')
gallery/view.py
class GalleryView(ListView):
model = Photo
template_name = 'gallery.html'
context_object_name = 'photos'
ordering = ('-date_posted')
album/url.py
app_name = 'album'
urlpatterns = [
path('useralbum/<str:username>/<int:pk>/', views.AlbumDetailView.as_view(), name='album'),
path('useralbum/<str:username>/', views.UserAlbumListView.as_view(), name='user_album_list'),
path('useralbum/<str:username>/create/', views.craete_album, name='create_album'),
path('<int:pk>/delete', views.delete_album, name='delete_album'),
path('<int:pk>/rename', views.rename_album, name='rename_album'),
path('<int:pk>/del_photo', views.del_photo, name='del_photo'),
path('add_photo', views.add_photo, name='add_photo'),
]
I have problem with editing form in django. Pls can someone help me?
I have form with 2 fields: description and image. By this form users can edit currect article data. For example add new several images to article or update/delete one of the currect image. To create image field I used django-multiupload app. Also I load data to server by ajax. I tried next code but it didnt show me currect images in image field. Only descrtiption field works fine and show me currect data. How to fix this problem?
models.py:
class Article(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
description = models.TextField(_('Description'))
class Image(models.Model):
article= models.ForeignKey(Article, on_delete=models.CASCADE)
image = models.FileField(_('Image'), upload_to='images/%Y/%m/%d/')
forms.py:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ('description', )
image = MultiFileField()
def save(self, commit=True):
instance = super(ArticleForm, self).save(commit)
return instance
views.py:
def article_edit(request, article_id):
data = dict()
article= get_object_or_404(Article, pk=article_id)
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES, instance=article)
if article_form.is_valid():
article.save()
data['form_is_valid'] = True
articles = Article.objects.all
context = {'articles': articles}
context.update(csrf(request))
data['html_article'] = render_to_string('project/article_list.html', context)
else:
data['form_is_valid'] = False
else:
article_form = ArticleForm(instance=article)
context = {'article_form': article_form}
data['html_article_form'] = render_to_string('project/article_edit.html', context, request=request)
return JsonResponse(data)
JS:
$(function () {
var loadForm = function () {
var btn = $(this);
$.ajax({
url: btn.attr("data-url"),
type: 'get',
dataType: 'json',
beforeSend: function () {
$("#modal").modal("show");
},
success: function (data) {
$("#modal .modal-content").html(data.html_article_form);
}
});
};
var saveForm = function () {
var form = $(this);
var dataForm = new FormData(form.get(0));
$.ajax({
url: form.attr("action"),
data: dataForm
type: form.attr("method"),
dataType: 'json',
success: function (data) {
if (data.form_is_valid) {
$("#article-list").html(data.html_article);
$("#modal").modal("hide");
}
else {
$("#modal .modal-content").html(data.html_article_form);
}
}
});
return false;
};
// Create Article
$("#article-add-button").click(loadForm);
$("#modal").on("submit", ".js-article-add-form", saveForm);
// Update Article
$("#article-list").on("click", "#js-edit-article-button", loadForm);
$("#modal").on("submit", ".js-article-edit-form", saveForm);
});
article_edit:
{% load widget_tweaks %}
<form method="post" enctype="multipart/form-data" action="{% url 'article_edit' %}" class="js-article-edit-form">
{% csrf_token %}
{% for field in article_form %}
<div class="form-group{% if field.errors %} has-danger{% endif %}">
<label class="form-control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" %}
{% for error in field.errors %}
<div class="form-control-feedback">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
I'm trying to create my own like button but I am encountering some issues.
The code use Ajax with Django and I am getting an error message but I don't know what is wrong.
here is the code, mostly inspired by this post.
article\models.py
class Article(models.Model):
user = models.ForeignKey(User, default='1')
titre = models.CharField(max_length=100, unique=True)
[... some unrelated models ...]
slug = models.SlugField(max_length=40)
likes = models.ManyToManyField(User, related_name="likes")
def __str__(self):
return self.titre
#property
def total_likes(self):
return self.likes.count()
article\urls.py
url(r'^like/$', views.like_button, name='like_button'),
article\views.py
#login_required(login_url='/user')
def like_button(request):
if request.method == 'POST':
user = request.user
id = request.POST.get('pk', None)
article = get_object_or_404(Article, pk=id)
if article.likes.filter(id=user.id).exists():
article.likes.remove(user)
else:
article.likes.add(user)
context = {'likes_count': article.total_likes}
return HttpResponse(json.dumps(context), content_type='application/json')
(As you can see I am trying to get the article id and not the slug)
article.html
<div>
{% for a in article %}
[... some unrelated html ...]
<input type="button" class="like" name="{{ a.id }}" value="Like" />
<p>count : {{ a.total_likes }}</p>
{% endfor %}
</div>
<script>
$('.like').click(function(){
$.ajax({
type: "POST",
url: "{% url 'like_button' %}",
data: {'pk': $(this).attr('name'), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
alert('Company likes count is now ' + response.count_likes);
},
error: function(rs, e) {
alert(rs.responseText); #No Article matches the given query.
}
});
})
</script>
the error say that No Article matches the given query.
What should I change in order to make this work?
From your code I can see that "id="like" is used in a for loop so it make multiple buttons with same ID. ID must be unique
Tryto change id with class like
<div>
{% for a in article %}
[... some unrelated html ...]
<input type="button" class="like" name="{{ a.id }}" value="Like" />
<p>count : {{ a.total_likes }}</p>
{% endfor %}
</div>
<script>
$('.like').click(function(){
var pk = $(this).attr('name');
$.ajax({
type: "POST",
url: "{% url 'like_button' %}",
data: {'pk': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
alert('Company likes count is now ' + response.count_likes);
},
error: function(rs, e) {
alert('Something went wrong.'); #getting this message.
}
});
})
</script>
I'm using django-voting application to let users vote on objects, and it works fine. But now that turns out there are more than one model to be voted, I decided to send extra options dynamically from template to the view to make it more generic and usable. But it doesn't work.
views.py
def vote_on_object(request, model, direction, post_vote_redirect=None,
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=loader, extra_context=None, context_processors=None,
template_object_name='object', allow_xmlhttprequest=False):
at the moment urls and template is like this and it works:
urls.py
link_dict = {
'model': Link,
'template_object_name': None,
'allow_xmlhttprequest': True,
}
url(r'^links/(?P<object_id>\d+)/(?P<direction>up|down|clear)vote/?$', vote_on_object, link_dict, name='vote_link'),
template.html
<form class="vote" method="POST" model="{{ object | model | lower }}" id="{{ object.pk }}" action="{% url 'vote_link' object_id=object.pk direction="up" %}">
{% csrf_token %}
<button type="submit"> + </button>
</form>
but when I change urls and template as below to send model dynamically to the views:
urls.py
link_dict = {
'template_object_name': None,
'allow_xmlhttprequest': True,
}
url(r'^vote/(?P<model>[-\w\d\_]+)/(?P<object_id>\d+)/(?P<direction>up|down|clear)vote/?$', vote_on_object, link_dict, name='vote_link'),
template.html
<form class="vote" method="POST" model="{{ object| model |lower}}" id="{{ object.pk }}" action="{% url 'vote_link' model=object|model object_id=object.pk direction="up" %}">
{% csrf_token %}
<button type="submit"> + </button>
</form>
I get following error: "POST /vote/Link/1/upvote HTTP/1.1" 500 11350
Also I should say model filter is a filter I've defined to extract model of object inside template.
Here's vote.js which handles AJAX voting:
$(document).ready(function() {
//listen for click
$('form.vote').on('submit', function(e){
e.preventDefault();
var vote_el = $(this);
var
url = vote_el.attr('action'),
model = vote_el.attr('model'),
direction = vote_el.attr('direction'),
object_id = vote_el.attr('id'),
allow_xmlhttprequest = "True" ;
console.log(allow_xmlhttprequest, url, model, direction, object_id);
$.ajax({
type:'POST',
url: vote_el.attr('action'),
data: {
'model': model,
'object_id': object_id,
'direction': direction,
},
dataType: "json",
success : function(data) {
console.log("Voted");
var pk = object_id;
$("p."+model+"-"+pk).text(data['score'].score);
}
});
});