django: modelChoiceField form not working in template - python

I am confronted for the first time to a situation where I want the user to select model column values based on some constraint. The goal is to have the user select something and then output the result below the form on the same page.
I am not sure what I am doing wrong but submiting the form output:
Select a valid choice. That choice is not one of the available choices.
Here is what I have been able to do:
forms.py
class SelectBuildingForm(forms.Form):
filename = forms.ModelChoiceField(queryset=Building.objects.none())
def __init__(self, *args, **kwargs):
us = args[1] or None
forms.Form.__init__(self, *args, **kwargs)
self.fields['filename'].queryset = Building.objects.filter(project=us).values_list('filename',flat=True)
views.py
#login_required
#owner_required
def FileSelectionView(request,pk):
form = SelectBuildingForm(request.POST or None, pk)
# if form.is_valid():
# filename = form.cleaned_data('filename')
# print(filename)
# return redirect('comparator_test')
return render(request,'files_selection.html', {'form':form})
and template
<div class="mt-5 md:col-span-2 md:mt-0">
<form method="POST" id="my-form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" value="Select">GOOOO</button>
</form>
{% if form.is_valid %}
{% for choice in form.cleaned_data.choices %}
<p>{{ choice }}</p>
{% endfor %}
{% endif %}
</div>
I have tried a couple options to validate the selection but none of them work. Can a pair of fresh eyes see where I am messing up?

Related

Django - User Search

I'm trying to filter ListView based on post method from search bar in my basetemplate. So making it works like:
Insert name --> SearchBar-->GET Method-->SearchView class(in views.py)--> render html with usernames.
I have done this, but it wont work. Could you please tell me what I'm doing wrong?
views.py in my user app
class SearchView(ListView):
model = User
template_name = 'blog/list_of_users.html'
context_object_name = 'all_search_results'
def get_queryset(self):
result = super(SearchView, self).get_queryset()
query = self.request.GET.get('search')
if query:
postresult = User.objects.filter(username__contains=query)
result = postresult
else:
result = None
return result
urls.py in my blog app
path('users_search/?search=<str:username>', user_view.SearchView.as_view(), name='user_search'),
search form in html
<form class="example" method="GET">
<input type="search" placeholder="ユーザー検索..." name="user_name">
<button type="submit">
検索
</button>
rendered html with user names
{% for result in all_search_results %}
{{ result.username }}
{% empty %}
add something to show no results
{% endfor %}
override get_context_data method
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user_name = self.request.GET.get('user_name', '')
context['all_search_results'] = User.objects.filter(username__icontains=user_name )
return context
In your template
<form class="example" method="GET">
<input type="text" placeholder="ユーザー検索..." name="user_name">
<button type="submit">
検索
</button>
</form>
{% for result in all_search_results %}
{{ result.username }}
{% empty %}
add something to show no results
{% endfor %}
Update:
in template, <input ........... name="search">
in views, user_name = self.request.GET.get('search', '')

Django Formset: object has no attribute 'get'

I think I'm almost there, but the last part just doesn't want to work :(. I hope you can help as I'm not seeing it anymore after 2 days.
Within my FormWizard I'm trying to (1) show a Formset based on a Slider Input in a previous set (setting the Extra value) and (2) in each Form within the Formset I want to show a ChoiceField (forms.Select) based on a Text-input in a previous step.
With a lot of searching on stackoverflow I am able to do step 1. Step 2 is almost working, except for the fact that the ChoiceField doesn't update with the new values from the Text-input. This is my code in views.py:
class FormWizardView(LoginRequiredMixin, SessionWizardView):
template_name = 'test/test.html'
def get_form_initial(self, step):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return formset
return super(FormWizardView, self).get_form_initial(step)
def done(self, form_list, **kwargs):
do something
return something
I'm trying to return the formset, but I get the error 'TestForm' object has no attribute 'get'. I am probably returning the wrong thing here, but whatever I try to return, it doesn't work. Returning super(FormWizardView, self).get_form_initial(step) just gives me the empty ChoiceField and returning the form gives me the error object of type 'TestForm' has no len().
I also printed out the form in my console, and that seems to work properly. Does anyone know what I should return in order to get the populated ChoiceField?
Many thanks!
EDIT:
Thanks for your answer! When I modify my get_form:
def get_form(self, step=None, data=None, files=None):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return super(FormWizardView, self).get_form(step, data, files)
I get the error ['ManagementForm data is missing or has been tampered with']. When browsing through StackOverflow it seems a template problem (and specifically not setting {{ wizard.management_form }}, but I took the plain code from the Django FormTools doc which should normally work. In my template I have this:
{% extends "base.html" %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{%
˓→trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{%
˓→trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}
Am I not seeing something in the template or my get_form function not correct? Many thanks for looking at my problem :)
Method get_form_initial from form wizard should return a dictionary with initial data for the form that will be created, not the form itself. If you want to modify whole form, try modifying get_form method instead.

Flask edit form submit truble

Objects
class MyObj:
def __init__(self, data_dict):
self.id = ''
self.name = ''
self.minDescription = ''
self.descriptions = ''
class MyObjForm(Form):
name = StringField('name')
minDescription = StringField('minDescription')
descriptions = TextAreaField('descriptions')
Routings
This good work in "POST" and "GET" mode. Submit button have a good reactions when click.
#app.route("/create", methods=["GET", "POST"])
#login_required
def create():
if request.method == 'POST':
form = MyObjForm(request.form)
if form.validate():
new_obj = MyObj(request.form)
return redirect(url_for("/"))
else:
return render_template('create.html', form=form)
else:
return render_template('create.html', form=MyObjForm())
When i route in "GET" mode my code is good work and view have old object data but click on submit button is not have any reactions.
#app.route("/edit/<id>", methods=["GET", "POST"])
#login_required
def edit(id):
if request.method == 'GET':
old_obj = d.get_by_id(id)
form = MyObjForm(obj=old_obj)
return render_template('create.html', form=form, id=id)
else:
#never entry
pass
HTML 'create.html'
<form method="post" role="form">
{{ form.csrf }}
{{ macros.render_field(form.name) }}
{{ macros.render_field(form.minDescription) }}
{{ macros.render_field(form.descriptions) }}
<input class="btn special" type="submit" value="Save"/>
</form>
I try this:
<form method="post" role="form" action="{{ url_for('edit', id=id) }}">
and this:
class MyObjForm(Form):
id = StringField()
any not have a progress :(
Wath wrong?
I identified the problem:
If not use args ('obj=new_obj') when MyObjForm create the save button call my route in post mode as well
When i published the question too simplified the description of the problem. Realy MyObjForm include BooleanField:
class MyObjForm(Form):
name = StringField('name')
minDescription = StringField('minDescription')
descriptions = TextAreaField('descriptions')
isArchive = BooleanField('Is not public')
I didn't notice the checkbox stopped showing up on the form for reasons unknown to me. The standard rendering method was used for output:
{{ macros.render_field2(form.isArchive) }}
{% macro render_field2(field) %}
{{ field.label }}
{{ field(**kwargs)|safe }}
{% if field.errors %}
{% for error in field.errors %}
{{ error }}
{% endfor %}
{% endif %}
{% endmacro %}
To solve the issue with the visualization applied the following:
<input type="checkbox" id="isArchive" name="isArchive">
<label for="isArchive">{{ form.isArchive.label }}</label>
{% for error in form.isArchive.errors %}
<li><font color="red">{{ error }}</font></li>
{% endfor %}
One last thing. Did not notice how, but the form also stopped working when you add an object, no longer pass validation. And the reason was {{ form.csrf }}} which had to be replaced with {{ form.csrf_token() }}. Still had to remove the validation parameters for BooleanField.
Now the problem is partially solved.I associate these issues with the installation of the flask-security component. Unfortunately, I was not able to make a normal display of the checkbox, but at least I can move on.
If anyone has any idea how these things can be related to each other or what I'm wrong please let me know. I do not want to leave gaps in knowledge.

django - What does this line achieve here?

I'm following a tutorial on effectivedjango.com, and this is the code they have:
views.py:
class CreateContactView(CreateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__' #this is needed for error msg Using ModelFormMixin (base class of CreateContactView) without the 'fields' attribute is prohibited.
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(CreateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-new')
return context
class UpdateContactView(UpdateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__'
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(UpdateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id})
return context
urls.py:
url(r'^$', contacts.views.ListContactView.as_view(),
name='contacts-list',),
url(r'^new$', contacts.views.CreateContactView.as_view(),
name='contacts-new',),
url(r'^edit/(?P<pk>\d+)/$', contacts.views.UpdateContactView.as_view(),
name='contacts-edit',),
contact_list.html:
{% block content %}
<h1>Contacts</h1>
<ul>
{% for contact in object_list %}
<li class="contact">
{{ contact }}
(edit)
</li>
{% endfor %}
</ul>
Add contact
{% endblock %}
edit_contact.html:
{% block content %}
{% if contact.id %}
<h1>Edit Contact</h1>
{% else %}
<h1>Add Contact</h1>
{% endif %}
<form action="{{ action }}" method="POST">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input id="save_contact" type="submit" value="Save" />
</form>
Back to list
{% if contact.id %}
Delete
{% endif %}
{% endblock %}
Why does the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py look like its calling itself?
What I mean is, this action is called when the submit button is pressed in the contact-edit template, correct? So it starts there, and it is reverse-calling contact-edit which is itself, right?
What am I not seeing here?
Thank you for all your help.
Yes, the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py is calling itself. This line generates the proper url for contacts-edit view.
This is done so that POST requests come to the same view i.e. UpdateContactView which is an UpdateView. There, proper handling will be done i.e. form validation will occur with the sent data. If the form is valid, object will be updated. Otherwise, the form will be displayed again with errors.
Django docs on UpdateView:
A view that displays a form for editing an existing object,
redisplaying the form with validation errors (if there are any) and
saving changes to the object.

Inline formset not rendering form field

I have models:
class MediaInfo(models.Model):
title = models.CharField(max_length=50,blank=True)
description = models.CharField(max_length=255,blank=True)
media_file = models.FileField(upload_to=get_upload_file_name)
def __unicode__(self):
return self.title
class Media(models.Model):
media_files = models.ForeignKey(MediaInfo) # I want ManyToManyField but it gives error saying no ForeignKey relation
Here I used Inline Formset to select multiple file.
What I wanted was I wanted a browser button on template and when I click that I could select multiple file or images with all those title and description informations.
For this I wrote a view:
def MediaAddView(request):
MediaInlineFormset = inlineformset_factory(MediaInfo, Media)
if request.method == "POST":
formset = MediaInlineFormset(request.POST, request.FILES)
if formset.is_valid():
formset.save()
return HttpResponseRedirect("someurl")
else:
return render_to_response("media_add.html", {"formset":formset,})
else:
formset = MediaInlineFormset()
return render_to_response("media_add.html", {"formset":formset,})
and my template media_add.html
{% block content %}
<form method="post" action="" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{form.id}}
<ul>
<li>{{form.media_file}}</li>
</ul>
{% endfor %}
<input type="submit" value="Submit" />
</form>
{% endblock %}
When I do this in my template I see nothing just 3 dots of list (li).
Like I said I wanted a browse button and when I click it I wanted to select and upload multiple files.
Whats wrong in here ? Can anyone guide me ?

Categories