In my project, I have 2 models Article and Quote. Every time, a user click a button in the home page, it will add the related quote into user's article.
The Article.models look like this:
class Article(models.Model):
user = models.OneToOneField(User, null=True)
quote = models.ManyToManyField(Quote, blank=True, null=True)
def __unicode__(self):
return self.user.username
Here is the view.py
def add_quote(request, id):
u = User.objects.get(username=request.user)
a = Article.objects.get(user=u)
q = Quote.objects.get(id=id)
a.quote.add(q)
a.save()
return HttpResponseRedirect(reverse("home"))
Home.html:
{% for quote in quotes %}
<p>{{ quote.description }}</p>
<p><a class="btn btn-primary" href="{{ quote.get_absolute_url }}"
role="button" data-container="body" data-toggle="popover"
data-placement="top" data-content="added">collect ยป</a></p>
It does work. However, it also reloads home page. So when I scroll down and click the button, the page goes back to the top and doesnt stay where i click.
I did some research finding out that dajax might help but just have no idea how to solve my problem with it or another efficient way?
There are two ways to go about this, and I recommend you implement them both.
1. Javascript-free method.
Include anchors for each quote, so when you redirect you can redirect like so:
HttpResponseRedirect(reverse("home")+"#quote_id_%s"%(q.id))
Which will look like:
http:example.com#quote_id_123
And jumps to the element with that id, like:
<blockquote id="quote_id_123">Four score and seven years ago...</blockquote>
This means that users who don't have Javascript (which is still a surprising amount) get the functionality of jumping to the right location.
To do this you can alter your for loop like so:
{% for quote in quotes %}
<p id="quote_id_{{quote.id}}">{{ quote.description }}</p>
<p><a class="btn btn-primary" href="{{ quote.get_absolute_url }}"
<!-- etc ... -->
{% endfor %}
2. Use progressive enhancement to add Javascript
This is less straight-forward, and will require writing a lot more code than above, but basically will require submitting the form via some ajax method, capturing the response (or error) correctly, and updating the page.
Dajax and jQuery will probably help in this regard, but will be very specific to your site.
Related
I have stored the rules in database in form of questions and want to ask questions by user and then conclude to a result.
I want questions to be displayed on user's screen with yes or no option and ask further questions according to the choices. i am unable to find a solution that how should i compare user input from front end with backend (django)
so far i have tried
views.py
def fetch_rules(request):
if request.method=='POST':
issueid=request.POST['issueid']
rules=Rules.objects.all().filter(parentissue=issueid)
return render(request,"questions.html",{"rules":rules})
else:
return HttpResponse("Not found")
template
{% extends 'base.html' %}
{% block content%}
<div class="questionwrapper">
{% for rul in rules %}
<div class="question">
<h1>{{rul.question}}</h1>
</div>
<div class="solution">
<p>{{rul.solution}}</p>
</div>
<p style="text-align: center;font-weight: bold;">Your issue solved?</p>
<div class="question_btns">
<a id="yes_q" >Yes</a>
<a id="no_q" >No</a>
</div>
{%endfor%}
</div>
{% endblock %}[![My template][1]][1]
currently template shows all the questions but i want to display once at a time and then display one after another according to the user's choice.
If you want to add content dynamically, you must use front-end technology. All you have to do is hide the questions using CSS display:none,then make onclick events on the buttons and a simple JS script will change the display of a specific child display:nth-child(n):inline-block
You may also do somethink similar using objects id, for example:
<div class="question" data-id={{rul.question.id}}>
<h1>{{rul.question}}</h1>
</div>
And now you can sort or display objects with data-id and JavaScript
I'm trying to add one more action to flask-admin forms.
It has to increment rating (+1) and it works with batch action, but not with single. Please help me find the bug, I've spent a lot of time trying to make this thing work properly.
Here's the code:
I made an html template in templates folder - custom_lists.html
{% extends 'admin/model/list.html' %}
{% block list_row_actions %}
{{ super() }}
<form class="icon" method="POST" action="/admin/user/action/">
<input id="action" name="action" value="approve" type="hidden">
<input name="rowid" value="{{ get_pk_value(row) }}" type="hidden">
<button onclick="return confirm('Are you sure you want to approve selected items?');" title="Approve">
<span class="fa fa-ok glyphicon glyphicon-ok"></span>
</button>
</form>
{% endblock %}
this succeeded with an icon on the list, but if i click to it - it says
Not Found
The requested URL was not found on the server. If you entered the URL
manually please check your spelling and try again.
added to templates folder and added to AdidasView class this:
list_template = 'custom_list.html'
#action('approve', 'Approve', 'Are you sure you want to approve selected items?')
def action_approve(self, ids):
try:
query = Adidas.query.filter(Adidas.id.in_(ids))
count = 0
for image in query.all():
image.rating += 1
count += 1
db.session.commit()
flash(ngettext('Item was successfully approved.',
'%s items were successfully approved.'%count,count))
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to approve items. %(error)s', error=str(ex)), 'error')
I have not changed the template but I have done it differently as following by setting the column_extra_row_actions variable and defining the action_play function
column_extra_row_actions = [
EndpointLinkRowAction('glyphicon glyphicon-play', 'event.action_play')
]
#expose('/action/play', methods=('GET',))
def action_play(self, *args, **kwargs):
return self.handle_action()
This solution does not seem to apply to this example, but I also struggled with a case where I received a 404 when I using an action on one item via the button, while the batch action worked as expected.
After taking a look at the JS for the batch action I realized that the two HTML forms for individual actions and batch actions are practically identical. The only difference is that when using batch actions there may be more input fields in the form. That implies that if you get a 404 on one, but not the other, there must be an error in your HTML.
In my case I was not aware that Flask-Admin addresses models_with_underscores_in_their_name as modelswithunderscoresintheirname. Therefore instead of
<form class="icon" method="POST" action="/admin/mymodel/action/">
my erroneous code was
<form class="icon" method="POST" action="/admin/my_model/action/">
Note the difference in the action field.
With this change I was able to use the #action API as explained in the Flask-Admin docs.
i have a sqlalchemy query which renders a template with a couple of settings.
below you can find very simplified code to give an idea of what is going on. This code puts a checkbox field for a setting on every page, and there is no fixed nr of settings at the moment, it depends on the size of the table. As far as the pagination goes, this works fine. I can go to next and previous page.
The submit button on the page only posts the checkbox value of the last page. Is it possible to also remember and/or save the input from all pages, not just the last page?
#app.route('/settings')
def settings():
page = request.args.get('page', 1, type=int)
settings = Settings.query.paginate(page, 1, False)
next_url = url_for('settings', page=settings.next_num) \
if settings.has_next else None
prev_url = url_for('settings', page=settings.prev_num) \
if settings.has_prev else None
inputtype = 'checkbox'
return render_template("settings.html",
settings = settings,
inputtype = inputtype,
next_url = next_url,
prev_url = prev_url
)
template would be something like this.
<div class="form-check">
{% for setting in settings %}
<input type="{{ inputtype }}" value="{{ setting }}" {{ setting }}
{% endfor %}
<div class=pagination>
{% if prev_url %}
Previous
(% endif %}
{% if next_url %}
Next
{% endif %}
</div>
<div class="panel-footer">
<input class="btn btn-primary" role="button" type="submit" value="Submit">
</div>
I get the feeling that if you submit you only submit the settings on the current page. Only the current settings are on the page and it would not make much sense to add all of them to the page.
I think that what you want is not possible on multiple pages if you use links to got to the previous and next settings.
If you make a change on page 1 and then click next the changes made on page 1 are not saved anywhere so they are lost.
Maybe it is possible to make previous and next also post to settings. This way you get the settings from that page and can make a temporary settings object that you can process when you click commit.
I fixed this without using javascript. I came across this answer and seems to do the trick. It simply does a post request and jumps to the next page.
#hugo, thanks for your answers, it certainly helped me looking in the right direction.
Cannot Generate a POST Request on Flask using url_for
all. I am working on a Flask application. I am developing a website for a restaurant with an online ordering system. I'm doing this for a friend and taking it as a learning experience. The issue I am going to ask about is probably a small one. I am not sure whether the issue is more with wtforms or flask-wtf. Hopefully someone can provide some insight.
I am creating a shopping cart, with a few buttons that are rendered through wtf.quick_form. Here is that:
<div id='cart'>
<div>
<p>
<center><b>Shopping cart</b></center>
</p>
<div>
{% for item in requested_item_record %}
<div id='cartI'>{{list_of_items[item]['name']}}</div>
<div id='cartB'> {{ wtf.quick_form(remove_item_dict[item]) }}</div>
{% endfor %}
</div>
<p id='cartT'>Subtotal: {{ total_cost/100 }} </p>
<p id='cartT'>Tax: {{ tax/100 }}</p>
<p id='cartT'>Total: {{ total/100 }}</p>
<div id='cartB'>{{ wtf.quick_form(clear_cart) }}</div>
<div id='cartB'>{{ wtf.quick_form(order_type) }}</div>
</div>
</div>
Here are the buttons defined by wtforms:
class AddItem(Form):
submit = SubmitField("Add item to cart")
class ClearCart(Form):
submit = SubmitField("Clear cart")
class RemoveItem(Form):
submit = SubmitField("Remove item")
class OrderType(Form):
select = SelectField("Pickup or delivery?", choices=[('pickup','Pickup'),('delivery','Delivery')])
submit = SubmitField("Proceed to checkout")
The problem is that I am seeing "Not a valid choice" below the SelectField from the OrderType button. I think this will confuse users. I am not sure how to remove it though. Here is a screenshot:
I only see the behavior if I submit data with one of the RemoveItem buttons or the ClearCart button.
Here is the relevant portion of my view function:
# generate add buttons for all items of the menu
button_dict = OrderedDict([])
for i in range(number_of_submit_buttons):
button_dict[i] = AddItem(prefix='add item ' + str(i))
clear_cart = ClearCart(prefix="clear cart")
# check for submit data from users, remove an item or clear cart.
for iterable, add_button in enumerate(button_dict):
if button_dict[add_button].validate_on_submit and button_dict[add_bu$
session['requested_item_record'].append(iterable)
session['total_cost'] += list_of_items[iterable]['price']
if clear_cart.validate_on_submit and clear_cart.submit.data:
session['requested_item_record'] = []
session['total_cost'] = 0
# select field, for delivery or pickup
order_type = OrderType(prefix='order_type')
if order_type.validate_on_submit() and order_type.submit.data:
if order_type.select.data == 'delivery' and session['requested_i$
return redirect(url_for('transaction.get_address'))
elif order_type.select.data == 'pickup' and session['requested_i$
return redirect(url_for('transaction.checkout'))
The functionality is fine. The website works how I want it to, just for some reason I am getting this error of "Not a valid choice". This appears only when I use one of the remove item or clear cart buttons.
The problem is, I think, in this line:
if order_type.validate_on_submit() and order_type.submit.data:
Here you unconditionally validate the OrderType form, so any time you click on the other buttons the validation for this form is going to fail due to the select drop down not having a value.
Try reversing the two parts of the conditional, as follows:
if order_type.submit.data and order_type.validate_on_submit():
In this way, the validation will only be done when the submit button for that form has value, which indicates that that's the form that was submitted by the user.
I am hitting a brick wall when it comes to solving this problem. I have a template that is being included in an other template, but I am unable to render any template variables in the included template. I have a separate template tag file for the included template. I am at a total loss right now how to resolve this problem.
I would like to be able to render the field values from my model in the template (which consist of an image, a short description, etc.) I am fairly certain that I am going about this in the wrong way.
Below is my code:
The model:
class AddOnItem(models.Model):
base_product = models.ForeignKey(Product)
base_price = models.DecimalField(max_digits=8, decimal_places=2, default=0.0)
addon_image = models.ImageField(upload_to='uploads/shop/product/images/',
default='uploads/shop/product/images/', help_text='Max width: 450px')
short_description = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return self.base_product.name
The template:
{% load addon_tags %}
{% if items_to_add %}
{% for items in items_to_add %}
<div id="addon-container">
<div id="left-addon" class="addon-container">
<img src="#" class="addon-image">
<p class="addon-description">{{items.short_description}}</p>
</div>
</div>
{% endfor %}
{% endif %}
addon_tags.py:
from django import template
from sho.models import AddOnItem
register = template.Library()
#register.inclusion_tag("foo/templates/v/sho/addon.html", takes_context=True)
def add_on_render():
context['items_to_add'] = AddOnItem()
return context
I imagine I am doing either a lot wrong (my assumption at the moment) or I am missing some minor bit. I have been working on this for several days and have gone over the docs repeatedly. I am simply completely missing something and this has become a major blocker for me. Any help would be appreciated.
Django version 1.4
Edit:
I ended up rewriting the view and did away with the templatetag. Thanks to both Daniel Roseman and Odif Yltsaeb for their replies.
1) From your post you are adding empty, new item into the context in add_on_render templateag.
2) I cant see in your post, WHERE you are using {% add_on_render %} templatetag. You have created templattag, but do not seem to be using it anywhere.
It is bit hard to understand what exactly are you trying to do or why you even need templatetag there.
If you want to display models field value, you do not need templateag for this and all the stuff that you show in your "template" part of this post, could be very well in your main template, which i assume, is not shown in your post.
If you want to use templatetag, then this templatetag should probably recieve AddOnItem istance as parameter like this:
#register.inclusion_tag("foo/templates/v/sho/addon.html", takes_context=True)
def add_on_render(item):
context['items_to_add'] = item
return context
And You could use it in some template like this:
{% load addon_tags %}
{% if items_to_add %}
{% for items in items_to_add %}
<div id="addon-container">
<div id="left-addon" class="addon-container">
<img src="#" class="addon-image">
<p class="addon-description">{% add_on_render items %}</p>
</div>
</div>
{% endfor %}
{% endif %}
and your foo/templates/v/sho/addon.html
would like like this:
{{ items_to_add.short_description }}
But doing it this way seems very unnecessary as you could achieve all that without templatag by using the template code that you already have outside your "main" template.
You haven't posted the template that you are attempting to include the tag in. I suspect you're not calling it at all, because there are a couple of errors that would cause exceptions if you did try and use the tag. You need to do {% add_on_render %} somewhere in your main template.
As I say though there are a couple of errors. Firstly, you don't define context (as an empty dict) before adding the items_to_add key. You can shortcut this by just doing it in one go.
Secondly you've made items_to_add a single, blank, AddOnItem. So in your included template, iterating through items_to_add does nothing at all. Not sure what you are trying to do there. Perhaps you want to pass all AddOnItem instances?
context = {'items_to_add': AddOnItem.objects.all()}
Or maybe you want to filter them by some criteria, in which case you probably want to pass those criteria to the inclusion tag itself:
def add_on_render(product):
context = {'items_to_add': AddOnItem.objects.filter(base_product=product)}
and you would call it from the main template like this:
{% add_on_render my_product %}
if you set "takes_context=True" you should take context as the first argument in decorated function:
#register.inclusion_tag("foo/templates/v/sho/addon.html", takes_context=True)
def add_on_render(context):
context['items_to_add'] = AddOnItem()
....