How to retain checkbox state after submit in django? - python

Checkbox has two states(checked and unchecked).In my view I have several products and I am trying to filter it based on its category.When I click on any checkbox, checkbox state is changing(getting unchecked).Also I am unable to select multiple checkboxes.
Models.py,
class Add_cat(models.Model):
category = models.CharField("Name")
cat_name = models.BooleanField(default=False)
My template file,
<head>
<script type="text/javascript">
function myfunction(){
document.getElementById("myform").submit();
}
</script>
</head>
<body>
<form action="{% url 'welcome_user' %}" id="myform">
{% csrf_token %}
<p >Categories</p>
{% for i in My_Cat %}
<input type="checkbox" name="cat_name" value="{{i.category}}"
onclick="return myfunction()"
{% if add_cat.cat_name %}checked="checked"{% endif %}>{{i.category}}
{% endfor %}
</form>
</body>
Views.py,
#Add_prod class contains product list with category as foreign key to Add_cat
def welcome_user(request):
categories = Add_cat.objects.all()
if 'cat_name' in request.GET:
filter_category = request.GET.getlist('cat_name')
my_products = Add_prod.objects.filter(cat__category__in = filter_category)
context = {
"My_Cat":categories,
"products":my_products
}
if 'cat_name' not in request.GET:
my_products = Add_prod.objects.all()
context = {
"My_Cat":categories,
"products":my_products
}
return render(request,"welcome-user.html",context)

Your for loop is iterating with i, but your if uses an add_cat, change one of them to be the same as the other.
{% for i in My_Cat %} --> {% for add_cat in My_Cat %}
{% if add_cat.cat_name %} --> {% if i.cat_name %}

{% for i in My_Cat %}
<input type="checkbox" name="{{i.category}}" value="{{i.category}}"
onclick="return myfunction()"
{% if add_cat.cat_name %}checked="checked"{% endif %}>{{i.category}}
{% endfor %}
You are using the same name for all the checkbox, so when you change the state of one, you change the state of all you need to use {{i.cat_name}} instead of cat_name. This is the reason for all your checkboxes changing the name when you click in one.
Btw, I don't know your requirements, but for me, the name of your attributes are quite confuse. I would spend more time to rethink the name of the attributes to avoid more errors like this one in the future.

Related

Django - Two forms on one page, how can I maintain URL parameters when either form is submitted?

I'm building an application that contains a list of meals, where each meal has various filters, a price, and a rating.
The filters are like tags; the user can select multiple, and the page only shows the meals that have the selected filters.
The price and ratings are integers, and the user can sort by either price or rating, which sorts the meals (cheapest -> most expensive for price, highest -> lowest for rating).
I have built two forms in Django, one for filters and one for sorting, and they both work on their own. However, let's say I submit the sorting form to sort by price; when I do this, it does sort by price, but it removes all of the prior filters I had submitted.
Below are the important pieces of code relevant to this problem:
views.py
def meals(request):
meal_list = Meal.objects.all()
tags = Tag.objects.all()
reviews = Review.objects.all()
filter_form = FilterForm(request.GET or None)
sorting_form = SortingForm(request.GET or None)
sort = ""
active_filters = []
if filter_form.is_valid():
tags = filter_form.cleaned_data.get('tags')
for tag in tags:
meal_list = meal_list.filter(tags__name=tag)
active_filters.append(tag)
if sorting_form.is_valid():
sort = sorting_form.cleaned_data.get('sort')
if sort == "price":
meal_list = meal_list.order_by('price')
else:
meal_list = meal_list.order_by('-rating')
paginator = Paginator(meal_list, 8)
page_number = request.GET.get('page')
meals_on_page = paginator.get_page(page_number)
context = {"meal_list": meal_list,
"distances": distances,
"tags": tags,
"reviews": reviews,
"active_filters": active_filters,
"meals_on_page": meals_on_page,
"filter_form": filter_form,
"sorting_form": sorting_form,
}
return render(request, 'meals/meals.html', context)
forms.py
from django import forms
# Tag is the model for the filters, it is just a ManyToManyField that contains a name attribute
from .models import Tag
class FilterForm(forms.Form):
tags = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(), widget=forms.CheckboxSelectMultiple)
class SortingForm(forms.Form):
SORT_CHOICES = [
('price', 'Price'),
('rating', 'Rating'),
]
sort = forms.ChoiceField(choices=SORT_CHOICES, widget=forms.Select)
meals.html
<form method="get">
{% for field in filter_form %}
{{ field.as_widget }} {{ field.label_tag }}
{% endfor %}
<input type="submit" value="Filter">
</form>
<form method="get">
{% for field in sorting_form %}
{{ field.as_widget }}
{% endfor %}
<input type="submit" value="Sort">
</form>
I have sadly way too long trying to fix this, and the closest I got was using get_copy = request.GET.copy() and then trying to manually add the URL parameters back onto the end of a URL after a form was submitted. However, none of my approaches using this seemed to work.
Thanks in advance for the help!
In your Django view, you can access the current URL parameters using the request object's GET attribute. To maintain these parameters when either form is submitted, you can include them in the form action attribute in your template.
For example, in your template, you can update the form action attribute to include the current URL parameters like this:
<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">
This will append the current URL parameters to the form action, so when the form is submitted, the current parameters will be included in the request.
In your view, you can then access these parameters using the request.GET dictionary. You can use these parameters to filter and sort your queryset accordingly before rendering the template.
Note: you should also check if the forms are valid before processing the form data to avoid unexpected behavior.
Also, you can use Django's forms.HiddenInput() to include the current parameters on your forms as hidden fields, that way you don't need to update form's action attribute.
I ended up solving this after soaking (way too many) more hours into it.
The way I did this was to build my own dictionary of parameters, and then to pass these parameters into the forms as hidden inputs.
Below is the added code:
views.py
get_copy = request.GET.copy()
parameters = get_copy.urlencode()
get_copy.pop('page', None)
param_list = parameters.split("&")
param_dict = defaultdict(list)
for param in param_list:
try:
key, value = param.split("=")
except ValueError:
# Handle the case where there's no "=" in the parameter string
key = param
value = ""
if key == "tags":
param_dict[key].append(int(value))
else:
param_dict[key].append(value)
# Django requires me to turn dictionary to items here, rather than in the template
param_dict_items = param_dict.items()
meals.html
<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">
{% for field in filter_form %}
{{ field.as_widget }} {{ field.label_tag }}
{% endfor %}
{% for key, value_list in param_dict_items %}
{% if key != 'tags' %}
{% for value in value_list %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
{% endif %}
{% endfor %}
<input type="submit" value="Filter">
<form method="get" action="{% url 'meals' %}">
{% for field in sorting_form %}
{{ field.as_widget }}
{% endfor %}
{% for key, value_list in param_dict_items %}
{% if key != 'sort' %}
{% for value in value_list %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
{% endif %}
{% endfor %}
<input type="submit" value="Sort">

Failed to get value from html page in Django

I have a problem with trying to get a response from my HTML page using Django (admin).
I have a pretty simple div = contenteditable and need to pass data from this div back after the submit button was clicked.
Everything, including choosing selection and opening the intermediate page works fine. But when I tapped submit button, the condition if "apply" in request.POST failed to work.
Please, tell me, what I'm doing wrong?
This is my Django admin:
class QuestionAdmin(AnnotatesDisplayAdminMixin, admin.ModelAdmin):
def matched_skills(self, question):
return ', '.join(s.name for s in question.skills.all())
def update_skills(self, request, queryset):
if 'apply' in request.POST:
print("something")
skills = []
for question in queryset:
skills.append(self.matched_skills(question))
return render(request,
'admin/order_intermediate.html',
context={'skills': skills})
update_skills.short_description = "Update skills"
This is my order_intermediate.html page:
{% extends "admin/base_site.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
<h1>Adjust skills. </h1>
{% for skill in skills %}
<div>
<div id="title" style="margin-left: 5px" contenteditable="true" > {{ skill }} </div>
</div>
{% endfor %}
<input type="hidden" name="action" value="update_status" />
<input type="submit" name="apply" value="Update skills"/>
</form>
{% endblock %}
Actually, request.POST is an HttpRequest object. For getting available keys in the body of the request, you need to use "request.POST.keys()" method. So, you can simply change your condition to:
if 'apply' in request.POST.keys():
print("something")
In my knowledge, you can not send div content with form submit. However you can use input tag with array in name attribute for this. This will send an array as post variable when submit
First, send skills as a enumerate object from your views
return render(request, 'admin/order_intermediate.html', context={'skills': enumerate(skills)})
Then edit your html to this (Note: if you have css in title id, change it to title class)
{% for i,skill in skills %}
<div>
<input class="title" name="skill[{{ i }}]" value="{{ skill }}" style="margin-left: 5px">
</div>
{% endfor %}
and handle array with any action you want to perform in update_skills()
for skill in request.POST.getlist('skill[]'):
# your code

How do you iterate through two items in a model and display on site using Django?

I have a models.py with the following fields:
class ChatStream(models.Model):
bot = models.TextField()
user = models.TextField()
name = models.CharField(max_length=100, null=True)
created_date = models.DateTimeField(auto_now_add=True)
And I'd like on a website to iterate through "bot" and "user" one at a time, so the site would hypothetically display something like:
bot: hello!
user: what's up?
bot: I'm good
user: What's your name
bot: bot is my name
.... etc. this would keep going...
So in my views.py I have
def displayDict(request):
m = ChatStream.objects.all()
return render(request, 'chatStream.html',
{"chat": m})
def send(request):
message = request.POST.get('userMessage', False)
ip = visitor_ip_address(request)
response = routes(message, ip)
print(ip, "user sent:", message, "bot response:", response)
chatItem = ChatStream(bot=response, user=message, name=ip)
chatItem.save()
return HttpResponseRedirect('/chat/')
Then in my template, chat.html I have
{% block chatStream %} {% endblock %}
And chatStream.html (this is where the error is happening I believe... how do you iterate through two items in the model so they appear one after the other on the html file?)
{% extends 'chat.html' %}
{% block chatStream %}
{% for a in bot%}
{% for b in user%}
<p>
<b>bot:</b> {{a}} <br>
<b>user:</b> {{b}} <br>
</p>
{% endfor %}
<form action="/send/" method = "post">{% csrf_token %}
<input type="text" name="userMessage">
<input type="submit" value="Send to smallest_steps bot">
</form>
{% endblock %}
But this does not work -- no text from the model is displayed on the site. I am not understanding how to iterate through two items within the model at once inside of the chatStream.html.
A lot going on here, lets try to break it down:
First, you need to pass context variables to your templates if you want to render them using the (jinja-like) Django template rendering system.
Your view function for rendering the template would look like this:
views.py
def render_chat_page(request):
# do some logic:
...
# pack the context variables:
context = {
'some_key' : 'some_value',
'chat_streams' : ChatStream.objects.all(),
...
}
return render(request, 'chat_page.html', context=context)
Ok, now that we've passed the context variables to the template, we can render html elements using the variables like so:
template.html
<div> The value of "some_key" is: {{some_key}} </div>
{% for chat_stream in chat_streams %}
<div> user says: {{chat_stream.user}}</div>
<div> bot says: {{chat_stream.bot}}</div>
{% endfor %}
This will render the user and bot messages for each ChatStream object. However my hunch is that this is not entirely what you're after, instead you may want something more dynamic.
In your displayDict view you're passing a QuerySet to the context. So, you need to loop over the QuerySet in your template.
{% extends 'chat.html' %}
{% block chatStream %}
{% for item in chat %}
<p>
<b>bot:</b> {{item.bot}} <br>
<b>user:</b> {{item.user}} <br>
</p>
{% endfor %}
<form action="/send/" method = "post">{% csrf_token %}
<input type="text" name="userMessage">
<input type="submit" value="Send to smallest_steps bot">
</form>
{% endblock %}

Why won't my view load in properly? (Django)

I am extremely new to Django's framework and recently finished the introductory series on their website, and I have tried adding a new view in which I use a form to create an object.
The problem is, whenever I access the view, it immediately executes the HttpResponseRedirect(), and second of all it does not even return a response.
views.py
def create(request):
context = {
'questionfields': Question.__dict__,
}
submitbutton = request.POST.get('Create', False)
if submitbutton:
new_question = Question(question_text=request.POST.get('question_text', ''), pub_date=timezone.now())
if new_question.question_text == '':
context = {
'questionfields': Question.__dict__,
'error_message': "No poll question entered."
}
del new_question
return render(request, 'polls/create.html', context)
else:
return HttpResponseRedirect(reverse('create'))
create.html
{% extends "polls/base.html" %}
{% block title %}Create a Poll{% endblock title %}
{% block header %}Create:{% endblock header %}
{% load custom_tags %}
{% block content %}
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:create' %}" method="post"> {% csrf_token %}
{% for field in questionfields %}
{% if field == 'question_text' %}
<label for="{{ field }}">{{ field|capfirst|replace }}:</label>
<input type="text" name="{{ field }}" id="{{ field }}">
<br>
{% else %}
{% endif %}
{% endfor %}
<br>
<input type="submit" value="Create" name="submit">
</form>
{% endblock content %}
I attempted to make it so if I enter text into the question_text input, when I click the submit button it creates a Question object with question_text being the entered text and the pub_date being the present time.
However, it merely gives a failed redirect.
I don't fully understand how the render() function works in a view and how its positioning in the logic affects the rendering of the view, so please excuse any of my mistakes.
I would like to know why it does not carry out any of the code from submitbutton... to else: , and how to fix this to get the view to work as intended. If anyone could help with my rendering and view problem it would be great.
Error image
You're using recursive here
return HttpResponseRedirect(reverse('create'))
In case it fails, it will redirect to this Create function again, and fail, and redirect again, again....
Try to do it like this:
def create(request):
context = {
'questionfields': Question.__dict__,
}
submitbutton = request.POST.get('Create', False)
if submitbutton:
new_question = Question(question_text=request.POST.get('question_text', ''), pub_date=timezone.now())
if new_question.question_text == '':
context = {
'questionfields': Question.__dict__,
'error_message': "No poll question entered."
}
del new_question
# Just render the page here with the initial context
return render(request, 'polls/create.html', context)

Form.media not being injected into a template

I wrote a simple app that is a custom DateField widget for mezzanine. Afaik it's, it's a simple case and besides overextending a template it would be the same in pure django or django-cms (feel free to correct me if I'm wrong).
The widget:
class DatePickerInput(forms.DateInput):
def __init__(self, attrs = None, format = None):
super(DatePickerInput, self).__init__(attrs, format)
self.attrs["class"] = "datepicker"
class Media:
css = { "all": ('css/ui-lightness/jquery-ui-1.10.3.custom.min.css',) }
js = (
"mezzanine/js/" + getattr(settings, "JQUERY_UI_FILENAME", "jquery-ui-1.9.1.custom.min.js"),
"js/datepicker_setup.js",)
I overextend base.html template to insert form.media:
{% overextends "base.html" %}
{% load pages_tags mezzanine_tags i18n future staticfiles %}
{% block extra_head %}{{ block.super }}
{{ form.media }}
{% endblock %}
Now, I create a form for my model class.
Here's the class:
class PlayerProfile(models.Model):
user = models.OneToOneField("auth.User")
# Can be later changed to use a setting variable instead of a fixed date
date_of_birth = models.DateField(default=date(1990, 1, 1))
Here's the model form:
from DateWidgets.widgets import DatePickerInput
from PlayerProfiles.models import PlayerProfile
class EditPlayerProfileForm(Html5Mixin, forms.ModelForm):
class Meta:
model = PlayerProfile
fields = ("date_of_birth", )
widgets = { 'date_of_birth': DatePickerInput }
def __init__(self, *args, **kwargs):
super(EditPlayerProfileForm, self).__init__(*args, **kwargs)
Here's the view:
#login_required
def profile_update(request, template="accounts/account_profile_update.html"):
"""
Profile update form.
"""
pform = forms.EditPlayerProfileForm
player_form = pform(request.POST or None, request.FILES or None, instance=request.user.playerprofile)
context = {"pform": player_form, "title": _("Update Profile"), "profile_user": request.user}
return render(request, template, context)
Here's the template:
{% overextends "accounts/account_profile_update.html" %}
{% load i18n mezzanine_tags %}
{% block main %}
<fieldset>
<legend>{{ title }}</legend>
<form method="post"{% if pform.is_multipart %} enctype="multipart/form-data"{% endif %}>
{% fields_for pform %}
<div class="form-actions">
<input class="btn btn-primary btn-large" type="submit" value="{{ title }}">
</div>
</form>
</fieldset>
{% endblock %}
Now if I view the form in a browser, the custom widget is there (I can tell because the input html tag has my custom class attribute value) but it doesn't inject the form media, it's missing. Any idea what's going on here? Thanks in advance! Cheers :-)
form isn't available in the template, because you've called your form variable pform.
Try {{ pform.media }}.
I was googled here with the same problem under different conditions and spotted another error that may appear under other conditions (my case is extending change_form.html in Django 2.1): I had to use extrahead instead of extra_head
{% block extrahead %}{{ block.super }}
{{ form.media }}
{% endblock %}

Categories

Resources