Create multiple choice field dynamically - python

My purpose is to create a django form to select devices filtered by country and club fields.
My form is this:
class MyForm(Form):
country = ChoiceField(choices=some_choices, initial=None)
club = CharField(widget=Select())
expiration_date = DateField()
sales_info = ChoiceField(choices=SALES_TYPES, initial=None)
devices = MultipleChoiceField(widget=CheckboxSelectMultiple(choices=[]))
def clean_devices(self):
devices = self.cleaned_data.get("devices")
if not devices:
raise ValidationError("At least one device must be selected.")
return devices
def save(self):
...
views.py
class MyFormView(LoginRequiredMixin, FormView):
template_name = "store/my_template.html"
form_class = MyFormView
def get_success_url(self):
return reverse('init_device')
def form_valid(self, form):
init_device = form.save()
if init_device:
return super().form_valid(form)
return super().form_invalid(form)
def form_invalid(self, form):
logger.error(form.errors)
my_template.html
<form action="{% url 'init_device' %}" method="post">
{% csrf_token %}
...
<select id="id_devices" name="devices" multiple>
I populate select field via javascript in this way:
let device_select = document.getElementById("id_devices");
serialIds.forEach(serialId => {
let option = document.createElement("option");
option.text = serialId;
option.value = serialId;
device_select.add(option);
});
I obtained filtered devices form db using a websocket by now I can't pass them to the form because this error raises:
ValueError: The view path.view didn't return an HttpResponse object. It returned None instead.
Then I print form.errors and this is showed <ul class="errorlist"><li>devices<ul class="errorlist"><li>Select a valid choice. device_attriubte is not one of the available choices.</li></ul></li></ul>
Can anyone help me?

Too much at once?
First get the user's choice of country and club and validate them as far as possible. When done, redirect to an URL such as
app/choose_device/country/club
and in the view which gets invoked, use kwargs country and club in the queryset of its form's ModelMultipleChoiceField. This normalizes to a list of model instances (device objects) when the form validates.
(I'm assuming that devices are objects in your DB. THis is implied by your use of "filtered" but isn't explicit).

Related

Why does my Submit button renders a page that is blank when it is supposed to contain the data that was just updated?

I'm trying to update the values of my database using a HTML Form.
When I Click Edit it brings me to the edit the values above.
However as I am clicking the submit button, it returns me a database but with no other values.
Is there anyone that can help me understand what I did wrong and point me to the right documentation (if any)
editclaims.html:
<div class="arrange2">
<h1>Edit Claim Form - #{{claims.id}} </h1>
</div>
<form method="POST" action="/update/{{claims.id}}">
{% csrf_token %}
views.py:
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
name = request.POST['name']
email = request.POST['email']
claim = request.POST['claim']
claimtype = request.POST.get('claimtype')
description = request.POST['description']
receipt = request.FILES['receipt']
cheque = request.POST.get('Cheque')
form = SaveClaimForm(name=name, email=email, claim=claim, claimtype=claimtype, description=description, receipt=receipt, cheque=cheque)
form.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
urls.py:
urlpatterns = [
path('existingclaims/', views.viewclaims, name='existingclaims'),
path('editclaims/<int:id>', views.editclaims, name='editclaims'),
path('update/<int:id>', views.updateclaims, name='updateclaims'),
]
It may not resolve all your problems but it will be more readable as answer.
When you get data from HTML then you create new object SaveClaimForm and it will have new ID and you will have the same object in two rows.
You have to get original Claim from database and update values in this object and save it - and then it will save it with original ID and you will have only one `object in database
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
# get original object
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
# update original object
claims.name = request.POST['name']
claims.email = request.POST['email']
claims.claim = request.POST['claim']
claims.claimtype = request.POST.get('claimtype')
claims.description = request.POST['description']
claims.receipt = request.FILES['receipt']
claims.cheque = request.POST.get('Cheque')
# save it with original `ID`
claims.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
BTW:
Django has special class ModelForm to create forms in HTML. It may also have methods to check if data in HTML are correct - ie. if fields are not empty, if email is correctly constructed (name#domain.com), if phone has only numbers, etc. So using ModelForm can be more useful then writing all manually in code.

Dynamically populate a choice field and get the non-chosen fields in the post data

I have a form where I'm trying to compare two objects (Cards) that are randomly selected from the database. In my form, I tried to dynamically populate a choicefield with the two cards, and in the view I'm trying to create an object (CardComparison(winning_card,losing_card)) with the choice of the user.
I've overwritten the __init__ of the form to dynamically populate the card choices, and it's working fine. The problem is that when the user selects a card, it only passes the selected card, and I'm not able to figure out which card is NOT chosen in the view.
I'm new to Django, and I've realized the more I struggle with this, a dynamic choice field may not be what I actually want to use, so a suggestion for a better method would also be greatly appreciated.
forms.py:
def get_new_comparison():
left_card = #get a random card (code hidden for cleanliness)
right_card = #get a second random card
left_card_choice = (left_card, left_card.name)
right_card_choice = (right_card, right_card.name)
return [left_card_choice, right_card_choice]
class CompareCardsForm(forms.Form):
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.fields['cards_to_compare'] =
forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
#Because everytime the __init__ is called the choices are randomized, I need to set the choices to the POST data's choices otherwise the selected card may not be in the form when it's trying form.is_valid()
else:
self.fields['cards_to_compare'] = forms.ChoiceField(choices=get_new_comparison(), widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(form.cleaned_data['cards_to_compare'])
# Here is where I want to create a new comparison using the two cards
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())
else:
form = CompareCardsForm()
return render(
request,
'mtg_compare/comparecard.html',
{'form': form}
)
The solution to this is actually by using the template file. By adding hidden fields in the template file and by setting the choices to an accessible variable in the form, you can pull the fields and add them to the POST message, so that I'm able to access them in the view.
comparecard.html:
{% extends "base_generic.html" %}
{% block content %}
<h1>Compare Cards</h1>
<h2>Choose a card to win the matchup</h2>
<p>
<p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.cards_to_compare }}
</table>
<input type="hidden" value="{{ form.card_list }}" name="var_name_1" />
<input type="submit" value="Submit" />
</form>
{% endblock %}
forms.py:
class CompareCardsForm(forms.Form):
def get_fresh_comparison():
return get_new_comparison()
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.card_list = card_choice
self.fields['cards_to_compare'] = forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
else:
self.card_list = get_new_comparison()
self.fields['cards_to_compare'] = forms.ChoiceField(choices=self.card_list, widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(request.POST.get('var_name_1'))
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())

invalid django form makes is_valid method always return false

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} )

Django: edit the choices set of forms.Select widget with dB data linked to current user in templates

Dear Python community,
Could you please share some insights with a newbie like myself regarding the following topic:
I would like to dynamically modify the inputs into form field input, specifically
forms.ChoiceField(choices=((text, name), widget=forms.Select())
As I was not able to access requestfrom class in forms.py, I'd like to try editing the choices from Django template engine. Is it possible to edit the choices, taking the parameters from views.py method using jinja?
The question is conceptual, a few lines of code as an example would be enough, I'll pick it up.
The tricky part is - the data should be dependent on logged-in User's created model instances.
If there's no actual way to do it via python, but js only - please let me know so I don't dry to do the impossible.
Thank you!
Code sample for reference:
forms.py
class InformForm(forms.Form):
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
template = forms.ChoiceField(choices=tuple([(template.text, template.name) for template in Template.objects.all()]),
widget=forms.Select(attrs={'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))
template
<form class="form-signin form-container" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-element-wrapper">
<div class="error-form-element">
<span class="error-span">{{field.errors}}</span>
</div>
<div class="form-label">{{field.label_tag}}</div>
<div class="form-data">{{field}}</div>
</div>
{% endfor %}
<button id="cr_inf" type="submit" class="btn btn-lg btn-primary btn-block stl-color"><span id="loader" class=""></span>Create inform</button>
</form>
views.py
class InformFill(View):
form_class = InformForm
temlate_name = 'distrib_db/inform_fill.html'
def get(self, request):
if request.user.is_authenticated():
form = self.form_class(None)
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
def post(self, request):
if request.user.is_authenticated():
form = self.form_class(user=request.user, data=request.POST)
if form.is_valid():
inform = Inform(flt_numbr=form.cleaned_data['flight_number'], date=form.cleaned_data['date'],
template=form.cleaned_data['text'], request=request)
inform.save()
date = form.cleaned_data['date']
flt_numbr = form.cleaned_data['flight_number']
try:
emails, contacts = get_mail_cnt(date, flt_numbr)
# inform = get_object_or_404(Inform, pk=request['pk'])
paxdata = PaxData(inform=inform, emails=' '.join(emails), contacts=' '.join(contacts))
paxdata.save()
return redirect('/inform/{0}/'.format(inform.pk))
# 'distrib_db:detail', context={'pk': inform.id}
except Exception as e:
return render(request, 'distrib_db/sample.html',
context={'date': date, 'flight_number': flt_numbr, 'error': e})
# return render(request, 'distrib_db/sample.html', context={'date': date, 'flt_numbr': flt_numbr})
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
QuerySet issue:
>>> usr = User.objects.filter(username='aleks')
sample = tuple([(template.text, template.name) for template in usr.template_set.all()])
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'template_set'
In InformForm class override __init__
def __init__(self, user, *args, **kwargs):
super(InformForm, self).__init__(*args, **kwargs)
self.fields['template'] = forms.ChoiceField(choices="make choice based on user")

Django form, view, and forms.py not working together

My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).
When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?
The code is below- my apologies if I neglected to include any relevant bits.
my forms.py:
from django import forms
from .models import Resto
class RestoSearch(forms.ModelForm):
class Meta:
model = Resto
fields = ('title', 'description', 'opening_hour', 'closing_hour')
models.py:
from django.db import models
class Resto(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
days_open = models.TextField(blank=True)
views.py:
from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch
def index(request):
return render(request, 'form.html')
def search(request):
form = RestoSearch()
if request.method == 'POST':
search_id=request.POST.get('textfield', None)
try:
#I think this is where my problem is
available = Resto.objects.filter(days_open = search_id)
html = ("<H1>Hello</H1>", available)
return HttpResponse(html)
except Resto.DoesNotExist:
return HttpResponse("Please try another day")
else:
return render(request, 'belize/form.html')
def restaurant_detail(request, id):
try:
restaurant = Resto.objects.get(id=id)
except Resto.DoesNotExist:
raise Http404('This restaurant does not exist')
return render(request, 'belize/restaurant_detail.html', {
'restaurant': restaurant,
})
template form.html:
<form method="POST" action="/search/">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Enter a day of the week</button>
</form>
I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be
def index(request):
form = RestoForm()
return render(request, 'form.html', {'form': form })
And then your template should change as
<form method="POST" action="/search/">
{% csrf_token %}
{{ form }}
<button type="submit">Enter a day of the week</button>
</form>
For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template
The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:
You are declaring a form that you never use.
You are trying to catch an exception that is never raised by .filter()
You filter condition will only work for exact matches.
I've annotated your code as well:
def search(request):
form = RestoSearch() # You aren't using this form anywhere in your code?
if request.method == 'POST':
# Where is 'textfield' coming from?
search_id = request.POST.get('textfield', None)
try:
# If search id is "Tuesday", and a restaurant is
# open on monday and tuesday, so it has "Monday,Tuesday"
# in the days_open field, then this search will not
# return any results, because its looking for an exact
# match
available = Resto.objects.filter(days_open=search_id)
html = ('<H1>Hello World</H1>', available)
return HttpResponse(html)
except Resto.DoesNotExist:
# This exception is not raised by .filter(),
# .filter() will return an empty list, [] if no results are found
# so this entire try/except is not doing anything
return HttpResponse("Please try another day")
else: # in your code, this else is not indented correctly
return render(request, 'belize/form.html')
So there is a lot going on here, lets try something simple, starting with the template:
{% if results %}
{% for restaurant in results %}
{{ restaurant }}
{% endfor %}
{% else %}
Sorry, no results for your search. Try again.
{% endif %}
<form>
{{ form }}
<input type="submit" name="Search" />
</form>
Next, the search form:
class SearchForm(forms.Form):
search_field = forms.CharField('Search', strip=True)
Finally the view:
from django.db.models import Q
def search(request):
form = SearchForm(request.GET)
results = [] # Set results to an empty list
if form.is_valid():
needle = form.cleaned_data['search_field'].capitalize()
results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
Q(days_open__endswith=',{}'.format(needle)) |
Q(days_open__contains=',{},'.format(needle)) |
Q(days_open='{},'.format(needle)) |
Q(days_open='{}'.format(needle)))
return render(request, 'search.html', {'results': results, 'form': form})
Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:
Either starts with Thursday, or
Ends with ,Thursday or
Contains ,Thursday, in the middle or
Has the value Thursday, or
Has the value Thursday
Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.
In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.
The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.

Categories

Resources