I created a tag like this:
#register.inclusion_tag('post/comment_block.html')
def limit_amount_in_a_page(page_nr=1, post_id=1, amount=5):
starting_index = page_nr*amount
for index in range(starting_index, starting_index + amount):
dosomething()
has_prev = (page_nr != 0)
has_next = ((page_nr + 1) * amount) < comments.count()
return {
something
}
The problem is : page_nr is always not an int.
and this is how I call the tag and assign the value to page_nr in the tag:
{% limit_amount_in_a_page page_nr=my_page_nr post_id=post.id amount=4 %}
this is where the value of my_page_nr comes from:
def to_post_page(request, post_id, page_nr):
post = get_object_or_404(Post, id=post_id)
form = CommentForm()
comments = Comment.objects.filter(pk=post_id)
return render(request, 'post/posts.html', {
'post': post,
'form': form,
'comments': comments,
'my_page_nr': page_nr,
})
this is the url calling the view:
url(r'^(?P<post_id>[0-9]+)/(?P<page_nr>[0-9]+)/$', views.to_post_page, name="post"),
{% for post in my_posts %}
<li>{{post.title}}</li>
{% endfor %}
The value passed to this url tag should be a int. As you can see, I passed a 0.
Really appreciate for any help!
The value for page_nr is extracted from the URL, and is therefore a string. If you need it to be an int, it's simple to convert it - you could do this in the view, for example:
return render(request, 'post/posts.html', {
'post': post,
'form': form,
'comments': comments,
'my_page_nr': int(page_nr),
})
Related
I have an Altair chart which I want to render in a Django Template. The chart is converted into a json object in the views. Here is the code
views.py
def home(request):
if request.method=='POST':
year = request.POST['year']
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
print(bar_obj)
context = {'bar': bar_obj}
return render(request, 'index.html', context=context)
return render(request, 'index.html')
template
<div id='altair-viz'>
{% if bar %}
{{ bar|safe }}
{% endif %}
</div>
This just prints the json in the template. I know I have to use Vega to render the graph but I am not sure how to do that in jinja syntax
A temp solution
One way I got this to work, is by creating a different view and calling that view in the template as follows
views.py
def renderAltair(request):
df, cus_dict = generate_df('2017')
bar_obj = barGraphAltair('2017', df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
template
<script>
vegaEmbed('#altair-viz', "{% url 'altair' %}")
</script>
This works, but as you can see from the original code, I get the year by submitting a form and passing that to the function for generating the graph. So I need the graph to be created in the home view
You can try this way.
def home(request):
if request.method=='POST':
year = request.POST['year']
context = {'year': year}
return render(request, 'index.html', context=context)
return render(request, 'index.html', {})
Not passing data in home view, will get that using json view.
template
<div id='altair-viz' data-url="{% url 'altair' %}?year={{year}}"></div>
<script>
var data_url = $('#altair-viz').attr('data-url');
vegaEmbed('#altair-viz', data_url)
</script>
and get data function
def renderAltair(request):
year = request.GET.get('year')
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
I am receiving the following error when submitting a form.
ValueError at /edit_entry/hi/
The view encyclopedia.views.edit_entry didn't return an HttpResponse object. It returned None instead.
Here is the views.py that is triggering the error.
def edit_entry(request, title):
if request.method == "POST":
form = NewEditEntryForm(request.POST)
if form.is_valid():
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
util.save_entry(title, content)
return HttpResponseRedirect("/wiki/" + title)
else:
form = NewEditEntryForm()
return render(request, "encyclopedia/edit_entry.html",{
"form": NewEditEntryForm(),
"title": title,
"content": util.get_entry(title)
})
What is the issue and how can I fix it?
(I also need help prepopulating the form with already existing data. I have tried using initial, but that has not worked. What is the best way to prepopulate the form with existing data?)
util.save_entry
def save_entry(title, content):
"""
Saves an encyclopedia entry, given its title and Markdown
content. If an existing entry with the same title already exists,
it is replaced.
"""
filename = f"entries/{title}.md"
if default_storage.exists(filename):
default_storage.delete(filename)
default_storage.save(filename, ContentFile(content))
sorry, I thought that you have a model.
# on util.py
def get_entry_content(title):
filename = f"entries/{title}.md"
return default_storage.open(filename).read()
# on views.py
def edit_entry(request, title):
if request.method == "POST":
form = NewEditEntryForm(request.POST)
if form.is_valid():
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
util.save_entry(title, content)
return HttpResponseRedirect("/wiki/" + instance.title)
else:
content = util.get_entry_content(title)
initial_dict = {
"title" : title,
"content" : content,
}
form = NewEditEntryForm(initial=initial_dict)
return render(request, "encyclopedia/edit_entry.html", {
"form": form,
})
All right, I think if this is not doing what you want, i would test the save_entry function in the console, creating and updating to see if it works or not.
When I try to submit my form I get this message:
The view views.save_flow_data didn't return an HttpResponse object. It returned None instead.
Views.py
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
else:
return render(request,'io/flow.html')
models.py
class CalcData(models.Model):
upload_flow = models.BooleanField(default=False)
water_flow_rate = models.DecimalField(max_digits=100, decimal_places=5)
water_flow_rate_unit = models.TextChoices('wfr_unit', 'm3/day m3/month')
datetime = models.DateTimeField(default=timezone.now)
submit button in form
<div>
<button action="{% url 'MyProject:save_flow_data' %}" type="submit" class="btn btn-light" style="width: 517.5px;" >Calculate</button>
</div>
urls.py
urlpatterns = [
path('', views.home, name='MyProject-home'),
path('io/flow/', views.io_flow, name='MyProject-io-flow'),
path('io/flow/save_flow_data', views.save_flow_data, name='save_flow_data')
]
I'm really not sure where to go from here. I had a perfectly working form but as soon as I scaled it up it flopped.
The case
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
is False is not covered.
You need to either remove the last else :
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
return render(request,'io/flow.html')
Or add a return to the uncovered path:
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
return render(request, 'io/flow.html') # Or return something else
else:
return render(request,'io/flow.html')
Remove else statement and keep indentation of:
return render(request,'io/flow.html')
At function level.
I meet difficulties as below :
I have a blog page. In blog ,i create 'comment' function to comment post. And comments has 'like' function. For this ,i create two view function ,one of them simple function ,second is api function. And create jquery ajax for to call api function. After api calling ,it update data in db. Problem is :
If i create two comment ,ajax function works only for first comment for to like comment. It looks like ,for first comment CommentLikeAPIToggle works ,for next comments CommentLikeToggle works. Here is my codes :
views.py
class CommentLikeToggle(RedirectView):
def get_redirect_url( self, *args, **kwargs):
id = self.kwargs.get('id')
obj = get_object_or_404(Comment,id=id)
url_ = obj.content_object.get_absolute_url()
user = self.request.user
if user.is_authenticated():
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
return url_
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
class CommentLikeAPIToggle(APIView):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request,id=None, format=None):
obj = get_object_or_404(Comment,id=id)
url_ = obj.get_absolute_url()
user = self.request.user
updated = False
liked = False
if user.is_authenticated():
if user in obj.likes.all():
liked = False
obj.likes.remove(user)
else:
liked = True
obj.likes.add(user)
updated = True
data = {
'updated':updated,
'liked':liked
}
return Response(data)
Ajax function :
function updateComLike (newCount,btn,verb){
btn.text(" "+newCount+ " " + verb);
btn.attr({"data-likes": newCount,"class":"fa fa-thumbs-up"})
}
$("#com-like").click(function(e){
e.preventDefault()
var this_ = $(this)
var likeUrl = this_.attr("data-href")
var likeCount = parseInt(this_.attr("data-likes"))
$.ajax({
url: likeUrl,
method: "GET",
data : {},
success: function(data){
var newLikes;
if (data.liked){
newLikes = likeCount + 1
updateComLike(newLikes,this_ ,gettext("Unlike"))
} else {
newLikes = likeCount - 1
updateComLike(newLikes,this_ ,gettext("Like"))
}
}, error: function(error){
}
})
})
Template tag :
{% for comment in comments %}
{{ comment.content }}
<footer>
<a data-href="{{comment.get_api_com_like_url}}" data-likes="
{{comment.likes.count}}" href="{{comment.get_com_like_url}}" id="com-like">
<i class="fa fa-thumbs-up"></i> {{comment.likes.count}}
{% if request.user in comment.likes.all %} {% trans "Unlike" %}
{%else%}{%trans "Like" %}{% endif %}
</a>
</footer>
{% endfor %}
Urls :
url(r'^api/(?P<id>\d+)/com-like/$',CommentLikeAPIToggle.as_view(), name='com-like-api-toggle'),
url(r'^(?P<id>\d+)/com-like/$',CommentLikeToggle.as_view(), name='com-like-toggle'),
I have found my problem and solved it myself. The problem is :i'm using id in template tags. And id should be unique to each element. So i used class instead of id and problem fixed
{{comment.likes.count}}" href="{{comment.get_com_like_url}}" class="com-like">
And in the ajax
$('a.com-like').click(function(e){
My django form is invalid and so the .is_valid method never returns true. As a result, I am getting an "Expected HttpResponse but received None" type of error because my code never executes what is within the if-condition. I am wondering how to make my form valid. I am new to django so I am probably missing something obvious. Here is my code:
views.py
template_name1 = 'multiplication/detail.html'
template_name2 = 'multiplication/multiplied.html'
class myForm(forms.Form):
quantity1 = forms.IntegerField(required=False)
quantity2 = forms.IntegerField(required=False)
form = myForm()
def get(request):
return render(request,template_name1,{'form': form} )
def multiply_two_integers(x,y):
return x*y
def post(request):
if (form.is_valid()):
x = request.POST.get('quantity1')
y = request.POST.get('quantity2')
product = multiply_two_integers(x, y)
return render(request, template_name2, {'form': form, 'product':
product })
template_name1
<h1>Multiplication Function</h1>
<form action = "{% url 'multiplication:post' %}" method = "post">
{{ form.as_p }}
{% csrf_token %}
<input type = "submit" value ="Multiply">
<!--<button type="submit"> Multiply </button>-->
<h1>{{product}}</h1>
</form>
template_name2
<h1>{{product}}</h1>
urls/multiplication
from django.urls import path
from multiplication import views
app_name = 'multiplication'
urlpatterns = [
# /multiplication/
path('', views.get, name = 'get'),
path('multiplied', views.post, name='post')
]
This code is very strange. You seem to have a set of functional views, but are trying to randomly use some concepts from class-based views.
The reason why your form is not valid is because you never pass any data to it; an unbound form cannot be valid. You should not be instantiating the form outside of a view; you need to do it in the view, and when the request is a POST you should pass the POST data to it.
In function-based views you should not define separate functions for get and post. Combine them, as sown in the Django docs.
There is another point that you have missed about the error message; your reaction to it telling you that you have not returned a response if the form is invalid is to ask "why isn't it valid", but you should also do what it says and return a response in this case; the form will sometimes be actually invalid, and you should deal with this case.
Finally, to get the data from the form you should use form.cleaned_data, not request.POST.
def multiply_two_integers(x,y):
return x*y
def my_view(request):
if request.method == 'POST':
form = MyForm(request.POST)
if (form.is_valid()):
x = form.cleaned_data['quantity1']
y = form.cleaned_data['quantity2']
product = multiply_two_integers(x, y)
return render(request, template_name2, {'product': product })
else:
form = MyForm()
return render(request,template_name1,{'form': form} )