I have a foreign key (zone_set) as a choice field in a form. It should only display current project's zone_set . As you can see, It is not the case since it's displaying a zone_set: I belong to an other project, I should not be displayed here which does not belong to the current project.
Here is my form but it doesn't work.
class ODMatrixForm(forms.ModelForm):
class Meta:
model = ODMatrix
# fields = '__all__'
exclude = ('size', 'locked',)
def __init__(self, current_project=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if current_project:
queryset = ZoneSet.objects.filter(project=current_project)
self.fields['zone_set'].queryset = queryset
The view creating the ODMatrix
def create_od_matrix(request, pk):
"""A roadnetwork depends on a project. It
must be created inside the project"""
current_project = Project.objects.get(id=pk)
form = ODMatrixForm(initial={'project': current_project})
if request.method == 'POST':
print(request)
od_matrix = ODMatrix(project=current_project)
# form = ODMatrixForm(request.POST, instance=od_marix)
form = ODMatrixForm(data=request.POST, instance=od_matrix)
if form.is_valid():
form.save()
messages.success(request, "OD matrix cessfully created")
return redirect('od_matrix_details', od_matrix.pk)
context = {
'project': current_project,
'form': form}
return render(request, 'form.html', context)
You created the __init__ as constructor of the Meta class, this should be a constructor of the ODMatrixForm, so:
class ODMatrixForm(forms.ModelForm):
# 🖟 part of ODMatrixForm, not Meta
def __init__(self, current_project=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if current_project:
self.fields['zone_set'].queryset = ZoneSet.objects.filter(
project=current_project
)
class Meta:
model = ODMatrix
# fields = '__all__'
exclude = ('size', 'locked',)
In the view, you will need to pass the current_project when you construct an ODMatrixForm object:
from django.shortcuts import get_object_or_404
def create_od_matrix(request, pk):
current_project = get_object_or_404(Project, pk=pk)
if request.method == 'POST':
form = ODMatrixForm(current_project, request.POST, request.FILES)
if form.is_valid():
form.instance.project = current_project
form.save()
messages.success(request, 'OD matrix cessfully created')
return redirect('od_matrix_details', od_matrix.pk)
else:
form = ODMatrixForm(current_project=current_project)
context = {
'project': current_project,
'form': form
}
return render(request, 'form.html', context)
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
Related
I want to get the form object from self.Form
This is my form
class ActionLogSearchForm(forms.Form):
key_words = forms.CharField(required=False)
and I set form as form_class, however I can't fetch the form data in view
class ActionLogListView(LoginRequiredMixin, ListSearchView):
template_name = "message_logs/action_log.html"
form_class = ActionLogSearchForm
def get_queryset(self):
res = []
form = self.form ## somehow form is None
print(form.cleaned_data) # error occurs here. 'NoneType' object has no attribute 'cleaned_data'
I think this is the simplest set, but how can I make it work?
Try this:
from django.http import HttpResponseRedirect
from django.shortcuts import render
class ActionLogSearchForm(forms.Form):
key_words = forms.CharField(required=False)
class ActionLogListView(LoginRequiredMixin, ListSearchView, request):
form_class = ActionLogSearchForm
def get_queryset(self, request):
res = []
if request.method == 'POST':
form = self.form(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/')
else:
form = NameForm()
return render(request, 'message_logs/action_log.html', {'form': form})
Want to have Django From dropdown filtered by logged-in User (Customer), using __init__ method. But when submit the form, keep having this Field id expected a number but got <QueryDict: { error:
class OrderForm(ModelForm):
class Meta:
model = Order
fields = ['contract', 'quantity', 'status']
def __init__(self, customer, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
self.fields['contract'].queryset = Contract.objects.filter(customer=customer)
#login_required(login_url='login')
def createOrder(request):
customer = request.user.customer.id
form = OrderForm(customer)
if request.method == 'POST':
form = OrderForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.customer = request.user.customer
form.save()
messages.success(request, 'Ticket submitted successfully .')
return redirect('customer_table')
context = {'form':form}
return render(request, 'create-order.html', context)
you can pass any attribute in form like this
in view:
form = OrderForm(request.POST, customer=request.user)
in form:
def __init__(self, *args, **kwargs):
customer = kwargs.pop('user') # allway before super()
# and call .pop() not .get() on kwargs, for upper class not evaluate user keyword
super(OrderForm, self).__init__(*args, **kwargs)
self.fields['contract'].queryset = Contract.objects.filter(customer=customer)
I summarize the working solutions as below:
View.py
def createOrder(request):
form = OrderForm(request.POST, customer=request.user.customer.id)
if request.method == 'POST':
form = OrderForm(request.POST, customer=request.user.customer.id)
if form.is_valid():
form = form.save(commit=False)
form.customer = request.user.customer
form.save()
messages.success(request, 'Ticket submitted successfully .')
return redirect('customer_table')
context = {'form':form}
return render(request, 'create-order.html', context)
class OrderForm(ModelForm):
class Meta:
model = Order
fields = ['contract', 'quantity', 'status']
def __init__(self, *args, **kwargs):
customer = kwargs.pop('customer')
super(OrderForm, self).__init__(*args, **kwargs)
self.fields['contract'].queryset = Contract.objects.filter(customer__id=customer)
I have a update view in django project. I need to override the post method because I am using multiple modelform.
views.py:
class EmployeeUpdateView(LoginRequiredMixin, UpdateView):
"""
Update a created a employee
"""
login_url = '/authentication/login/'
template_name = 'employee/employee_update_form.html'
form_class = EmployeeAddModelForm
work_form_class = WorkExperienceForm
education_form_class = EducationForm
queryset = Employee.objects.all()
#success_url = reverse_lazy('employee:employee-list')
def get(self, request, *args, **kwargs):
id_ = self.kwargs.get("id")
employee_id = Employee.objects.get(id=id_)
work_info = WorkExperience.objects.get(employee=employee_id)
education_info = Education.objects.get(employee=employee_id)
return render(request, self.template_name, {
'form': self.form_class(instance=employee_id),
'work_form': self.work_form_class(instance=work_info),
'education_form': self.education_form_class(instance=education_info)
}
)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
work_form = self.work_form_class(request.POST, prefix='work_form')
education_form = self.education_form_class(request.POST, prefix='education_form')
# Check form validation
if form.is_valid() and work_form.is_valid() and education_form.is_valid():
instance = form.save()
work = work_form.save(commit=False)
education = education_form.save(commit=False)
work.employee = instance
education.employee = instance
work.update()
education.update()
return redirect('employee:employee-list')
return render(request, self.template_name, {
'form': form,
'work_form': work_form,
'education_form': education_form
}
)
When I press update button of my form, error is giving showwing "This field already exist". It means when i update the form is post data as a new form not as a update form.
Where is error in my post method?
I used this tutorial to successfully set up a dynamic inline formset using Django. The CreateView works great, but I cannot get the UpdateView to actually update the related fields. There are no errors thrown, but the items will not update. I believe I have isolated the error to the form_valid function. The code is as follows. Thank you for any assistance.
class ApplicantCreate(CreateView):
model = Applicant
success_message = 'Your application was submitted successfully.'
form_class = forms.ApplicantForm
template_name = 'careers/add_applicant.html'
success_url = reverse_lazy('careers:thanks')
def get_context_data(self, **kwargs):
data = super(ApplicantCreate, self).get_context_data(**kwargs)
positions = super(ApplicantCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['employer'] = forms.ApplicantEmployerFormSet(
self.request.POST,
prefix='employer')
data['education'] = forms.ApplicantEducationFormSet(
self.request.POST,
prefix='education')
else:
data['employer'] = forms.ApplicantEmployerFormSet(prefix='employer')
data['education'] = forms.ApplicantEducationFormSet(prefix='education')
return data
context['unfilled_positions'] = Position.objects.filter(filled=False)
return positions
def form_valid(self, form):
context = self.get_context_data()
employer = context['employer']
education = context['education']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if employer.is_valid():
employer.instance = self.object
employer.save()
if education.is_valid():
education.instance = self.object
education.save()
return super(ApplicantCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('careers:thanks')
class ApplicantUpdate(SuccessMessageMixin,LoginRequiredMixin,GroupRequiredMixin,UpdateView):
group_required = [u'careers-admin',u'careers']
model = Applicant
success_message = '%(first_name)s %(last_name)s was updated successfully.'
form_class = forms.ApplicantUpdateForm
template_name = 'careers/edit_applicant.html'
def get_context_data(self, **kwargs):
data = super(ApplicantUpdate, self).get_context_data(**kwargs)
positions = super(ApplicantUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['employer'] = forms.ApplicantEmployerFormSet(
self.request.POST,
instance=self.object,
prefix='employer')
data['education'] = forms.ApplicantEducationFormSet(
self.request.POST,
instance=self.object,
prefix='education')
else:
data['employer'] = forms.ApplicantEmployerFormSet(
instance=self.object,
prefix='employer')
data['education'] = forms.ApplicantEducationFormSet(
instance=self.object,
prefix='education')
return data
context['unfilled_positions'] = Position.objects.filter(filled=False)
return positions
def form_valid(self, form):
context = self.get_context_data()
employer = context['employer']
education = context['education']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if employer.is_valid():
employer.instance = self.object
employer.save()
if education.is_valid():
education.instance = self.object
education.save()
return super(ApplicantUpdate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('careers:applicant_detail',kwargs={'pk': self.object.pk})
This is not a good blog post, because no errors are shown if any of the inline forms aren't valid. As a user, I wouldn't expect the view to just silently ignore errors in the inline forms, saving my main instance successfully and not reporting back those errors.
Note that I don't know what the errors are, maybe it's just an issue with the management form. But in any case, formset errors should be handled before the main object is actually saved.
In general, if you need to write a view with multiple forms (including a formset), it's better to use a function-based view or a View where you write the get and post than trying to force this into a generic class-based view, which is not meant for this.
def multiple_forms_view(request, object_id):
# if this is a POST request we need to process the form data
obj = get_object_or_404(MyModel, pk=object_id)
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = MyForm(request.POST, instance=obj)
formset = MyFormSet(request.POST, instance=obj, ...)
# check whether it's valid:
if form.is_valid() and formset.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = MyForm(instance=obj)
formset = MyFormSet(instance=obj, ...)
return render(request, 'name.html', {'form': form, 'formset': formset})
That way your template can render the errors of each form, including those in the formset. As mentioned before, you could also do this in a View (so your mixins will work), just write the get and post methods and return the same as in the function-based view above.
I have a FormView called LeagueTransferView based on a form LeagueTransferForm.
I'm trying to override get_context_data to add extra players to render in the template.
But get_context_data is never reached. It's working fine on other views like, DetailView, ListView,...
I'm missing something?
Below my configuration
View
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
print('----NEVER REACHED----')
context = super(LeagueTransferView, self).get_context_data(**kwargs)
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=petitioner)
return context
def get(self, request, *args, **kwargs):
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form = self.form_class(initial={'league': self.kwargs['pk'], 'petitioner': petitioner})
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
transfer = form.save(commit=False)
team = Team.objects.filter(league=transfer.league, players__in=[transfer.requested_player])
if not team: # free agent
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
print(team)
transfer.player_owner = team[0]
if transfer.petitioner.user is None: # bot team
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
messages.success(request, _('transfer waiting for confirmation by player owner'))
transfer.save()
return HttpResponseRedirect(reverse('hockey_dashboard'))
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
FORM
class LeagueTransferForm(forms.ModelForm):
class Meta:
model = Transfer
fields = ['league', 'requested_player', 'offered_player', 'player_owner', 'petitioner']
labels = {
'requested_player': _('Requested player'),
'offered_player': _('Offered player'),
}
widgets = {
'requested_player': forms.HiddenInput,
'league': forms.HiddenInput,
'player_owner': forms.HiddenInput,
'petitioner': forms.HiddenInput
}
Your code is never reaching get_context_data() because you have overridden the get() method and not calling the get_context_data() function there. You need to manually call the get_context_data() function at the time of passing context to render() in your code.
Instead of doing that, i would suggest you to try the below approach where instead of overrriding get() and returning your custom response, you only override what is necessary and let Django handle the rest.
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
context = super(LeagueTransferView, self).get_context_data(**kwargs)
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=self.petitioner)
return context
def get_initial(self):
initial = super(LeagueTransferView, self).get_initial()
initial['league'] = self.kwargs['pk'] # add custom data to initial
initial['petitioner'] = self.petitioner # add custom data to initial
return initial
def get_form(self, form_class=None):
form = super(LeagueTransferView, self).get_form(form_class)
# override the queryset
form.fields['offered_player'].queryset = self.petitioner.players
return form
def get(self, request, *args, **kwargs):
# only perform 1 query to get 'petitioner'
self.petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
return super(LeagueTransferView, self).get(request, *args, **kwargs)