I am currently trying to show a custom form where I alter the form in the corresponding view
in the .html
<form action="{% url 'systems_system_update' system.id %}" id="system_update_form" method="post" class="form">
{% csrf_token %}
{% buttons %}
<button type="submit" class="btn btn-primary">Update {{ system.name }}</button>
{% endbuttons %}
{% bootstrap_form system_update_form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Update {{ system.name }}</button>
{% endbuttons %}
</form>
So I'm trying to show system_update_form where it is defined in the view
in SystemDetailView
class SystemDetailView(DetailView):
"""Detail view for Systems"""
form_class = SystemForm
model = System
template_name = 'systems/system_detail.html'
def get_form(self, form_class):
form = super(SystemDetailView, self).get_form(form_class)
form.fields['primary_purpose_business_use'].label = "Primary purpose/business use"
form.fields['secondary_purpose_business_uses'].label = "Secondary purpose/business uses"
return form
def get_context_data(self, **kwargs):
context = super(SystemDetailView, self).get_context_data(**kwargs)
context.update({
'system_update_form': self.form_class(instance=self.get_object()),
'object_name': self.object.name,
'user_can_edit': self.request.user.has_perm(
'services.change_system'),
'user_can_delete': self.request.user.has_perm(
'services.delete_system'),
'object_events': self.object.events.all(),
})
return context
So, I'm updating the context and setting 'system_update_form' to the form and I'm trying to update the form by using get_form, but I don't think DetailView has the get_form method for overriding.
Updating it in the forms is not an option because SystemForm is used in many different places and needs to be altered for this view specifically
DetailView does not have a get_form() method as it does not uses a form, thereby your get_form() method is not being called.
Instead of that, you can manually instantiate the form in your get_form() method and call this method when generating the context.
Also, in your code, you are passing instance to the form by calling self.get_object(). This will lead to another query for getting the object as Django has already fetched the object before. Instead of doing that, you can directly pass object using self.object.
class SystemDetailView(DetailView):
def get_form(self):
form = self.form_class(instance=self.object) # instantiate the form
# modify the form fields
form.fields['primary_purpose_business_use'].label = "Primary purpose/business use"
form.fields['secondary_purpose_business_uses'].label = "Secondary purpose/business uses"
return form
def get_context_data(self, **kwargs):
context = super(SystemDetailView, self).get_context_data(**kwargs)
context.update({
'system_update_form': self.get_form(), # get the form instance
'object_name': self.object.name,
'user_can_edit': self.request.user.has_perm(
'services.change_system'),
'user_can_delete': self.request.user.has_perm(
'services.delete_system'),
'object_events': self.object.events.all(),
})
return context
Related
I have a table with atrributes, and I'm displaying each attribute as a checkbox in html view. I want to show them in different pages, but I don't want to make different functions for each category. Is there an efficient way to do so? Here is what I tried so far.
def questions(request):
# start session page for the user to test
questions = Attribute.objects.all()
realistic = Attribute.objects.filter(holland_code=1)
investigative = Attribute.objects.filter(holland_code=2)
artistic = Attribute.objects.filter(holland_code=3)
social = Attribute.objects.filter(holland_code=4)
enterprising = Attribute.objects.filter(holland_code=5)
conventional = Attribute.objects.filter(holland_code=6)
left = [realistic, investigative, artistic, social, enterprising, conventional]
for attribute in left:
# get all the values form the form submitted
if request.method == "POST":
# THIS WILL GET ALL THE RECOMMENDAITONS
rAttributes = request.POST.getlist('realistic')
print(rAttributes)
return render(request, "main/questions.html", {"questions": attribute})
context = {
"questions": realistic,
}
return render(request, 'main/questions.html', context)
This is my html template to display the checkboxes
<form action="" method="POST">
{% csrf_token %}
<div class="form-check">
{% for question in realistic %}
<input type="checkbox" class="form-check-input" id="exampleCheck1" name="realistics" value="{{ question.attribute_name }}">
<label class="form-check-label" for="exampleCheck1">{{ question.attribute_name }}</label>
<br>
{% endfor %}
</div>
<input type="submit" class="btn btn-danger" value="Next">
</form>
Just add the form to the base template and extend that template so it'll be available everywhere.
or you could create a class based view and add a post request
class InsterViewNameHere(View):
def post(request, self, id=None, *args, **kwargs):
#form logic and context here
context = {}
return render(request, 'template.html', context)
To not have to repeat the same post function for every view or class-based-view, you could create a Mixin view
class ObjectnameMixin(object):
model = ClassModel
form = formname
def post_form(self):
return form
I found multiple answers to this same questions but unfortunately, I can't seem to figure it out :(
The form has a drop-down list for the 'subcategory' field in my model 'PhysicalPart', the values of the 'subcategory' field are updated dynamically upon the form creation (using a 'category' parameter).
Unfortunately, I can't get the drop-down to show all subcategories AND have the one from the database selected at the same time. I can't seem to retrieve the 'short_description' value either from the database.
It used to work before I learned about UpdateView class and decided to use it instead...
Any insight on how-to workaround my problem would be appreciated!
forms.py
class PartForm(forms.ModelForm):
subcategory = forms.ChoiceField(choices=[])
class Meta:
model = PhysicalPart
fields = ['subcategory', 'short_description']
views.py
class PartUpdate(UpdateView):
model = PhysicalPart
template_name = 'part_update.html'
form_class = PartForm
def post(self, request, *args, **kwargs):
# Load model instance
self.object = self.get_object()
# Load form
form = super(PartUpdate, self).get_form(self.form_class)
# Populating subcategory choices
form.fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Check if form valid and save data
if form.is_valid():
form.save()
return redirect('part-list')
# Update context before rendering
context = self.get_context_data(**kwargs)
context['part_id'] = self.object.pk
context['part_category'] = self.object.category
context['manufacturing_list'] = self.object.manufacturing.all()
return render(request, self.template_name, context)
html
<form action="{% url 'part-update' pk=part_id category=part_category %}" method="post" style="display: inline">
{% csrf_token %}
<div class="form">
<p class="font-weight-bold">Type</br>
{{ form.subcategory }}
</p>
</div>
<div class="form">
<p class="font-weight-bold">Short Description</br>
{{ form.short_description }}
</p>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
<form action="{% url 'part-list' %}" style="display: inline">
<button type="submit" class="btn btn-danger">Cancel</button>
</form>
My problem was that I did not differentiate the "GET" versus the "POST" calls in the UpdateView class, I was trying to do everything in the post() method. It took me a while to figure it out but now I think it's clear.
I originally used the get() method but I realize that get_context_data() was better suited as it automatically loads most of the context (eg. the instance and the form), instead of having to do everything from scratch in the get() method.
Scrubbing through the code of the UpdateView class here, it also seemed necessary to add ModelFormMixin into the declaration of the PartUpdate class so that the get_context_data() method automatically loads the form associated to the target model/instance (else it looks like it won't do it).
Here is my updated views.py code:
class PartUpdate(UpdateView, ModelFormMixin):
model = PhysicalPart
template_name = 'part_update.html'
form_class = PartForm
success_url = reverse_lazy('part-list')
def get_context_data(self, **kwargs):
# Load context from GET request
context = super(PartUpdate, self).get_context_data(**kwargs)
# Get id from PhysicalPart instance
context['part_id'] = self.object.id
# Get category from PhysicalPart instance
context['part_category'] = self.object.category
# Add choices to form 'subcategory' field
context['form'].fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Return context to be used in form view
return context
def post(self, request, *args, **kwargs):
# Get instance of PhysicalPart
self.object = self.get_object()
# Load form
form = self.get_form()
# Add choices to form 'subcategory' field
form.fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Check if form is valid and save PhysicalPart instance
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
From my understanding you are trying to edit an instance. This is how you do it in Django, it should autopopulate your inputs with the proper values :
my_record = MyModel.objects.get(id=XXX)
form = MyModelForm(instance=my_record)
More details on this answer : how to edit model data using django forms
If your models are properly done (with relations) you shouldn't need to provide the choices for the Select.
In my project, i have a template where i'm trying to put two forms for different use cases. I've never come across this problem before, so i don't really know where to go from here to use two forms in the same page.
At first i thought of creating another view to handle each form, but i think that this solution would create problems with the rendering of my templates, other than not being sustainable if i should have this problem again with another template.
After making some research, i found a solution but it works for class based views, but i'd like to avoid that since my view is already a function based view, and i would have to make a lot of changes in my code. However, if CBV is the best way to go, i can make the change.
Every advice is appreciated
First field
class FirstForm(forms.ModelForm):
firstfield = forms.CharField()
secondfield = forms.CharField()
class Meta:
model = MyModel
fields = ("firstfield", "secondfield")
def save(self, commit=True):
send = super(FirstForm, self).save(commit=False)
if commit:
send.save()
return send**
Second Form
class SecondForm(forms.ModelForm):
firstfield = forms.FloatField()
secondfield = forms.Floatfield()
thirdfield = forms.CharField()
class Meta:
model = MyModelTwo
fields = ("firstfield", "secondfield", "thirdfield")
def save(self, commit=True):
send = super(SecondForm, self).save(commit=False)
if commit:
send.save()
return send
Template
<h3> First Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
<h3> Second Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
views.py
def myview(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = FirstForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
send = form.save()
send.save()
messages.success(request, f"Success")
# if a GET (or any other method) we'll create a blank form
else:
form = FirstForm()
return render(request,
"main/mytemplate.html",
context={"form":form})
I have been told to use a context in my view, but i don't know how to integrate it in my view. Is this a doable solution, or is there a better way to do this?
context = {
'first_form': TradingForm(request.POST or None),
'second_form': LimitSellForm(request.POST or None),
}
Here's one approach. Add a name attribute to your buttons, like this:
<button name="button1" type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
...
<button name="button2" type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
Then in your view, you can check which form has been submitted by looking for the button name in the post:
def myview(request):
if request.method == 'POST':
if 'button1' in request.POST:
form1 = FirstForm(request.POST)
if form1.is_valid():
# do what needs to be done and redirect
if 'button2' in request.POST:
form2 = form = SecondForm(request.POST)
if form2.is_valid():
# do what needs to be done and redirect
else:
form1 = FirstForm()
form2 = SecondForm()
return render(request, "main/mytemplate.html",
context={'form1': form1, 'form2': form2})
you can use TemplateView instead for normal view function and add this below
def get_context_data(self, **kwargs):
context = {
'first_form': TradingForm(request.POST or None),
'second_form': LimitSellForm(request.POST or None),
}
you can check in the documentation:
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#templateview
I'm trying to filter foreign key field selections in my model form, but form isn't working. My scripts:
forms.py
from django import forms
from .models import Album, Song
class SongCreateForm(forms.ModelForm):
class Meta:
model = Song
fields = [
'album',
'name',
'is_favorite'
]
widgets = {
'album': forms.Select(attrs={'class': 'form-control'}),
'name': forms.TextInput(attrs={'class': 'form-control'}),
'is_favorite': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
def __init__(self, user, *args, **kwargs):
super(SongCreateForm, self).__init__(*args, **kwargs)
self.fields['album'].queryset = Album.objects.filter(owner=user)
views.py
from django.views.generic import CreateView
class SongCreateView(CreateView):
template_name = 'music/song_create.html'
success_url = '/songs/'
def get_form(self, form_class=None):
form_class = SongCreateForm(user=self.request.user)
return form_class
print(form_class.errors.as_data())
print(form_class.is_valid())
song_create.html
{% extends 'base.html' %}
{% block content %}
<form method="post">{% csrf_token %}
{% if form.errors %}
<p>{{ form.errors }}</p>
{% endif %}
<div class="form-group">
<label for="{{ form.album.id_for_label }}">Album</label>
{{ form.album }}
</div>
<div class="form-group">
<label for="{{ form.name.id_for_label }}">Name</label>
{{ form.name }}
</div>
<div class="form-check">
<label for="{{ form.is_favorite.id_for_label }}" class="form-check-label">
{{ form.is_favorite }}Favorite
</label>
</div>
<button type="submit" class="btn btn-primary" value="Save">Add</button>
</form>
{% endblock %}
Filtering queryset of 'album' field is working correctly. It is displaying only albums that are associated with authenticated user, but when I click submit in form browser doesn't redirect me to success url and song isn't added to database. I've added two print statements at the end of views.py to check that form is valid and has any errors. While form.errors returning empty dictionary, form.is_valid() is equal to False. Why Django treats this form as invalid, if it doesn't have any errors?
Note: SongCreateView works successfully, when I comment out init function in forms.py and get_form function in views.py.
First of all, remove those print statements after the return statement as they will never get executed after a value is returned by the method. Secondly, add form_class attribute to your view class like this:
class SongCreateView(CreateView):
template_name = 'music/song_create.html'
success_url = '/songs/'
form_class = SongCreateForm
Then in get_form method:
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
return form_class(user=self.request.user, **self.get_form_kwargs())
Then override form_valid method to associate the logged in user with the Song instance like this:
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return redirect(self.get_success_url())
Import redirect from django.shortcuts at the top. Hope it helps!
My guess is that the ID of Song is missing. When you use the magic bit of code
{{ form }}
this field is added automatically, although the input is hidden. When you define all fields manually like you are doing you will have to make sure the ID is added, that is you need to add
{{ form.pk }}
or whatever the name (here pk) of the id of your song is.
Im trying to build a sidebar form on a website in order to search some stuff, kind of like this http://cl.ly/0e0R1T0G3B1x0c451F22 where a person can search for something given certain parameters in any part of the website, meaning that the form must be displayed everywhere, and thats where I have the problem.
it seems that the view is not passing the form to the sidebar on the website, what can I do to always send the empty form as a sidebar.
Im trying to be as clear as possible, yet I know that It might not be enough, please let me know, Ill clarify.
This is the view
#render_to(template='league/common_fragments/sidebar_fixturesandresults.html')
def results_fixt_search(request):
results_fixt_search_form = results_fixt_SearchForm(request)
return {'results_fixt_search_form': results_fixt_search_form, }
This is the form, note that Im using django-uni-form
class HorizRadioRenderer(forms.RadioSelect.renderer):
""" this overrides widget method to put radio buttons horizontally
instead of vertically.
"""
def render(self):
"""Outputs radios"""
return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
class results_fixt_SearchForm(forms.Form):
league_search = forms.ChoiceField(choices=[ (league.slug, league.title ) for league in League.objects.all()])
radios = forms.CharField(widget=forms.RadioSelect(renderer=HorizRadioRenderer,
choices=(('table','Table'),('results','Results'), ('fixtures', 'Fixtures'),)), required=True)
# uniForm Helper
helper = FormHelper()
layout = Layout(
Fieldset('',
'league_search', 'radios'
)
)
helper.add_layout(layout)
# Submit button(s)
submit = Submit('submit','Submit')
helper.add_input(submit)
class Meta:
fields = ['search_term', 'radios']
def __init__(self, request, *args, **kw):
super(results_fixt_SearchForm, self).__init__(*args, **kw)
self.request = request
this is the HTML template
<form action="{% url results_fixt_search %}" method="get" enctype="multipart/form-data" id="results_fixt_search" class="select-form">
{% with results_fixt_search_form.helper as helper %}
{% uni_form results_fixt_search_form helper %}
{% endwith %}
</form>
And this the URL
url(r'^(?i)results_fixt/search/$', 'results_fixt_search', {}, name='results_fixt_search'),
You can either include the form html directly (without using django forms for rendering):
<form action = '{% url search %}' method='GET'>
<label>Query: <input type='search' name='query'></label>
<input type='submit' value='Go'>
</form>
or pass the form instance to all views using context processor:
def search_form_processor(request):
return {'search_form': SearchForm()}
and then render the form:
<form action = '{% url search %}' method='GET'>
{{ search_form.query.label_tag }} {{ search_form.query }}
<input type='submit' value='Go'>
</form>
Form may be processed by its own view at its own url then.