My current code allows me to render a queryset of Comments (parent comments) as well as replies to those comments. But i'm unable to render replies to those replies. My goal is to have an infinite reply system. Here's my code:
class Comment(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
destination = models.CharField(default='1', max_length=12, blank=True)
parent_id = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', related_name='replies', related_query_name='replies', blank=True, null=True)
comment_text = models.TextField(max_length=350, blank=True, null=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
children = models.IntegerField(default=0)
def __str__(self):
return str(self.comment_text)
my parent comment view:
def user_comment(request):
if request.is_ajax():
comment = CommentForm(request.POST or None)
ajax_comment = request.POST.get('text')
id = request.POST.get('id')
comment_length = len(str(ajax_comment))
if comment.is_valid() and request.user.is_authenticated:
comment = Comment.objects.create(comment_text=ajax_comment, destination=id, user=request.user)
username = str(request.user)
return JsonResponse({'text': ajax_comment, 'text_length': comment_length,
'username': username, 'id': comment.id})
else:
return HttpResponse()
and my reply view:
def comment_reply(request):
if request.is_ajax():
comment = CommentForm(request.POST or None)
reply_text = request.POST.get('reply_text')
id = request.POST.get('id')
parent_id = request.POST.get('parent_id')
parent = Comment.objects.get(id=parent_id)
parent.children += 1
parent.save()
if comment.is_valid() and request.user.is_authenticated:
comment = Comment.objects.create(comment_text=reply_text, destination=id, user=request.user, parent_id=parent_id, parent_comment=parent)
username = str(request.user)
return JsonResponse({'reply_text': reply_text, 'username': username})
else:
return HttpResponse()
ajax calls
var str = window.location.href.split('?')[0];
var path = str.split("/")[4];
$('.comment_form').on('submit', function(e) {
e.preventDefault();
var c = $(this).find('.comment_text').val()
console.log('this:', c);
$.ajax({
type: 'POST',
url: '/user_comment/',
data: {
text: $(this).find('.comment_text').val(),
id: path,
csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val(),
},
success: function(data) {
if(data.text == '') {
console.log('Cannot submit blank comment');
} else {
//console.log('')
$('.commentsContainer hr').prepend("<div class='comment_div'><div class='left_comment_div'>" +
"<div class='username_and_votes'><h3><a class='username_foreign'>" + data.username +
"</a></h3></div><br><p>" + data.text +
"</p></div></div>");
}}
});
});
// reply comment
$(document).on('submit', '.reply_comment_form', function(e) {
e.preventDefault();
parent_id = $('.reply_comment_form').data('comment_id');
$.ajax({
type: 'POST',
url: '/comment_reply/',
data: {
reply_text: $(this).find('.comment_text').val(),
parent_id: parent_id,
id: path,
csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val(),
},
success: function(data) {
$('.reply_comment_form').replaceWith("<div class='comment_div new_comment' style='display: inline-block;'><div class='left_comment_div'>" +
"<div class='username_and_votes'><h3><a href='#' class='username_foreign'>" + data.username +
"</a></h3><br><p>" + data.reply_text +
"</p></div></div>");
}
});
});
and the comments html
<div class="commentsContainer">
<form action="" class="comment_form">{% csrf_token %}
{{ comment.comment_text|add_class:"comment_text" }}
{{ comment.id }}
<input type="submit" value="Comment" class="comment_submit">
</form>
<hr>
{% for i in comment_list %}
<div class='comment_div' data-comment_id="{{ i.id }}">
<div class="left_comment_div">
<div class="username_and_votes">
<h3><a class='username_foreign'>{{ i.user }}</a></h3>
</div>
<br>
<p>{{ i.comment_text }}</p>
</div>
</div>
{% for reply in i.replies.all %}
<div class='comment_div new_comment' data-comment_id="{{ reply.id }}">
<div class="left_comment_div">
<div class="username_and_votes">
<h3><a class='username_foreign'>{{ reply.user }}</a></h3>
</div>
<br>
<p>{{ reply.comment_text }}</p>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
If someone could give me advice how to implement endless replies from the code I currently have that would be great.
You need to turn your comments loop into a separate template
{% for i in comments %}
<div class='comment_div' data-comment_id="{{ i.id }}">
<div class="left_comment_div">
<div class="username_and_votes">
<h3><a class='username_foreign'>{{ i.user }}</a></h3>
</div>
<br>
<p>{{ i.comment_text }}</p>
</div>
</div>
{% include 'comments_template.html' with comments=i.replies.all %}
{% endfor %}
Then you just call this where you need it with
{% include 'comments_template.html' with comments=comment_list %}
Related
I want to create add to favourites feature in ListView but I am struggling with passing product slug in each item.
I have a table with the product name and Action (Add/Remove buttons). I want to allow users to add a specific product to favourites or remove it from there.
models.py
class Product(models.Model):
[...]
favourite = models.ManyToManyField(User, default=None, blank=True, related_name='favourite_product')
slug = models.SlugField(unique=True, blank=True, max_length=254)
views.py
#login_required
def products_in_favourites(request, slug):
product = get_object_or_404(Product, slug=slug)
added_to_favourite = False
if product.favourite.filter(id=request.user.id).exists():
product.favourite.remove(request.user)
added_to_favourite = False
else:
product.favourite.add(request.user)
added_to_favourite = True
context = {
'object': product,
'added_to_favourite': added_to_favourite,
}
if request.is_ajax():
html = render_to_string('favourite.html', context, request=request)
return JsonResponse({'form': html})
class AddToFavourite(LoginRequiredMixin, ListView):
template_name = "AddToFavourite.html"
model = Product
queryset = Product.objects.all()
context_object_name = 'product_list'
def get_context_data(self, **kwargs):
context = super(AddToFavourite, self).get_context_data(**kwargs)
product_item = get_object_or_404(Product, slug=self.kwargs['slug']) # how to pass slug of each product item here?
added_to_favourite = False
if product_item.cart.filter(id=self.request.user.id).exists():
added_to_favourite = True
context['added_to_favourite'] = added_to_favourite
AddToFavourite.html
{% block body %}
<section class="container">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody id="favourite">
{% for product in product_list %}
<tr>
<td>{{product.title}}</td>
<td>
{% include 'favourite.html' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endblock body %}
{% block javascript_files %}
<script>
$(document).ready(function(event){
$(document).on('click', '#favourite', function(event){
event.preventDefault();
var slug = $(this).attr('value');
$.ajax({
type: 'POST',
url: '{% url "products_in_favourites" product.slug %}',
data: {
'slug':slug,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
dataType: 'json',
success: function(response){
$('#favourite-section').html(response['form'])
},
error: function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>
{% endblock javascript_files %}
favourite.html
<form action="{% url 'products_in_favourites' product.slug %}" method="POST" class="mx-3">
{% csrf_token %}
{% if added_to_favourite %}
<button type="submit" id="favourite" name="product_slug", value="{{product.slug}}">Remove</button>
{% else %}
<button type="submit" id="favourite" name="product_slug", value="{{product.slug}}">Add</button>
{% endif %}
</form>
urls.py
urlpatterns = [
path('add-to-favourite/', views.AddToFavourite.as_view(), name='cost_calculator'),
path('add-to-favourite/<slug:slug>', views.products_in_favourites, name='products_in_favourites'),
]
You are sending your slug not in a ListView you are sending it
<form action="{% url 'products_in_favourites' product.slug %}" method="POST" class="mx-3">
to products_in_favourites function, and doing it wrong at recieving part,
You are not sending it to a function as slug, you are sending it as data with post reqeust, and so you should recieve it same way:
#login_required
def products_in_favourites(request):
#print all data to see what is coming to this view
print(request.POST)
slug = request.POST['product_slug']
product = get_object_or_404(Product, slug=slug)
Also it is better to pass pk (primary key) instead of slug, primary key has indexes by default in database, so your query will be much much faster if you use pk.
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);
}
});
});
When i click add to cart i have the following error in views.py:
"In updateItem, productId = data['productId'] KeyError: 'productId' "
views.py line 70 where i have my error:
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
print('Action:', action)
print('productId:', productId)
customer = request.user.customer
product = Product.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer, complete=False)
orderItem, created = OrderItem.objects.get_or_create(order = order, product = product)
if action == 'add':
orderItem.quantity = (orderItem.quantity +1)
elif action == 'remove':
orderItem.quantity = (orderItem.quantity -1)
orderItem.save()
if orderItem.quantity <=0:
orderItem.delete()
return JsonResponse('El item fue agregado', safe=False)
carrito.js:
function updateUserOrder(productId, action){
console.log('Usuario logeado y enviando data...')
var url = '/update_item/'
fetch (url, {
method: 'POST',
headers:{
'Content-Type':'application/json',
'X-CSRFToken': csrftoken,
},
body:JSON.stringify({'productId': productId, 'action':action})
})
.then((response) =>{
return response.json()
})
.then((data) =>{
console.log('data:', data)
location.reload()
})
}
My template carrito.html:
When i click on the button Add to cart i have the issue.
{% extends 'tienda/index.html' %}
{% load static %}
{% block content %}
</div>
{% for item in items %}
<div class="cart-row">
<div style="flex:2"><img class="row-image" src="{{item.product.imageURL}}"></div>
<div style="flex:2"><p>{{item.product.name}}</p></div>
<div style="flex:1"><p>{{item.product.price|floatformat:2}}</p></div>
<div style="flex:1">
<p class="quantity">{{item.quantity}}</p>
<div class="quantity">
<img data-product={{item.product.id}} data-action="add" class="chg-quantity update-cart" src="{% static 'images/arrow-up.png' %}">
<img data-product={{item.product.id}} data-action="remove" class="chg-quantity update-cart" src="{% static 'images/arrow-down.png' %}">
</div>
</div>
<div style="flex:1"><p>${{item.get_total}}</p></div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
The above block of code is where i have the buttons with the actions.
The json.loads() function accepts only unicode strings.
So first try to decode the request body with request.body.decode('utf-8') and next to load it with json.loads().
decoded_data = request.body.decode('utf-8')
data = json.loads(decoded_data)
There you can read the docs about json.loads
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>
Question I want to be able to add a multiple Dimensions(Dimension Class) to my Sheet Class. What I would like to do is pass it into angular then back to Django if possible I just need help/guidance on how to do this I have looked a countless blogs and still can't come up with correct way of doing so any help would be greatly appreciated.
Here is my sheet model and dimension model (model.py)
class Sheet(models.Model):
drawing_number = models.CharField(max_length=255)
drawing_revision = models.CharField(max_length=255)
heat_number = models.CharField(max_length=255)
note = models.CharField(max_length=255)
valc = models.CharField(max_length=255)
class Dimension(models.Model):
description = models.CharField(max_length=255)
style = models.CharField(max_length=255)
target = models.IntegerField()
upper_limit = models.IntegerField()
lower_limit = models.IntegerField()
Here is my view.py with sheet_from_create method and my test method for adding a dim that isn't working.
def sheet_form_create(request):
if request.method == 'GET':
sheet_form = SheetForm()
else:
sheet_form = SheetForm(request.POST)
cust_int = Customer.objects.latest('id').id
cust_int_plus = int(cust_int) + 1
c_date = datetime.now()
u_date = datetime.now()
if sheet_form.is_valid():
drawing_number = sheet_form.cleaned_data['drawing_number']
drawing_revision = sheet_form.cleaned_data['drawing_revision']
heat_number = sheet_form.cleaned_data['heat_number']
note = sheet_form.cleaned_data['note']
valc = sheet_form.cleaned_data['valc']
try:
get_cust = Customer.objects.filter(customer_name=customer_name).exists()
except:
get_cust = False
if get_cust == True:
c = Customer.objects.get(customer_name=customer_name)
c_id = c.customer_id
else:
creat_cust = Customer.objects.create(id=cust_int_plus, customer_id=cust_int_plus, customer_name=customer_name)
c_minus = cust_int_plus
c = Customer.objects.get(id=c_minus)
c_id = c.customer_id
sheet = Sheet.objects.create(
drawing_number=drawing_number,
drawing_revision=drawing_revision,
heat_number=heat_number,
note=note,
valc=valc,
customer_id=c_id)
return render(request, 'app/sheet.html')
return render(request, 'app/sheet_form_create.html', {
'sheet_form': sheet_form,
'title':'New Sheet',
})
I tried this with ajax no luck It will not render my dim.html
def add_dimensions(request):
data = {}
if request.method == 'POST':
dim_form = DimForm(request.POST)
if dim_form.is_valid():
dim = Dimension()
description = dim_form.cleaned_data.get['description']
style = dim_form.cleaned_data.get['style']
target = dim_form.cleaned_data.get['target']
upper_limit = dim_form.cleaned_data.get['upper_limit']
lower_limit = dim_form.cleaned_data.get['lower_limit']
dim.save()
data['description'] = dim.description;
data['style'] = dim.style;
data['state'] = "ok";
return HttpResponse(json.dumps(data), mimetype="application/json")
else:
data = 'fail'
return render(request, 'app/sheet_form_create.html')
else:
dim_form = DimForm()
return render(request, 'app/dim.html', {'dim_form': dim_form})
Here is my sheet_form_create.html this is where I want my dim.html to render inside of as well
{% extends "app/layout.html" %}
{% load crispy_forms_tags %}
{% block content %}
<br />
<br />
<br />
<div class="row" >
{% crispy sheet_form %}
</div>
<body ng-app="dim_app">
<!-- render my dim.html here somehow -->
<div ng-controller="DimCtrl">
<div class="data">
</div>
<button ng-click="save()">Click</button>
</div>
</body>
{% endblock %}
Here is my dim.html
{% extends "app/layout.html" %}
{% load crispy_forms_tags %}
{% block content %}
<br />
<br />
<br />
<div class="row" >
{% crispy dim_form %}
</div>
{% endblock %}
Last but not least my angular code that I have so far for testing purposes here is where I am most confused
dim_app = angular.module('dim_app', []);
dim_app.controller('DimCtrl', ['$scope', function($scope) {
$scope.num = 0;
$scope.save = function () {
$(".data").html("click:" + $scope.num);
//some how load dim.html in here how ever many times its clicked example they click 5 times render dim.html five times and save each on as a child record of sheet
$scope.num += 1;
};
}]);
This is an example of what I would like it to look like
I figured it out.
Here is my angular code
dim_app = angular.module('dim_form', [])
.controller('dim_ctrl', ['$scope', '$compile', function ($scope, $compile) {
$scope.show_dim = function () {
var comp = $compile("<div </div>")($scope);
$("#id").append(comp);
};
}])
.directive('myDim', function () {
return {
templateUrl: '/sheet/sheet_form_create.html/_dim'
};
});
Here is my sheet_form_create.html
{% extends "app/layout.html" %}
{% load crispy_forms_tags %}
{% block content %}
<br />
<br />
<br />
<div class="row" >
{% crispy sheet_form %}
</div>
<body ng-app="dim_form">
<div ng-controller="dim_ctrl">
show
<div id="d"></div>
</div>
</body>
{% endblock %}