I'm creating a home page for my Django webapp, outside of Django admin. I'd like the home page to have a very simple ModelForm that when submitted, writes to the database.
I'm getting the following error at /home/ currently and not sure how to resolve it.
AttributeError at /home/ 'ActionCodeForm' object has no attribute
'is_bound'
I know about bound and unbound forms and have read the docs, but I am not sure how to actually implement them.
Here is my model:
class ActionCode(models.Model):
action_code = models.CharField(blank=False, max_length=10,
verbose_name="Action Code")
Here is my ModelForm:
class ActionCodeForm(ModelForm):
class Meta:
model = ActionCode
fields = ('action_code',)
def __init__(self, *args, **kwargs):
super(ActionCodeForm).__init__(*args, **kwargs)
Here is my view:
def action_code_form(request):
if request.method == 'GET':
form = ActionCodeForm()
else:
form = ActionCodeForm(request.POST)
if form.is_valid():
action_code = form.cleaned_data['action_code']
form.save()
else:
form = ActionCodeForm()
return render('action_code_form.html', {'form': form},
context_instance=RequestContext(request))
And here is my template, action_code_form.html:
<form method="post" action="">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit"/>
</form>
And urls.py:
from home.views import action_code_form
urlpatterns = [
url(r'^home/', action_code_form, name="home"),
]
You need to check if the form is valid only if the method is POST. Also, the first param of render() must be request
Your view should be as follows:
def action_code_form(request):
form = ActionCodeForm()
if request.method == 'POST':
form = ActionCodeForm(request.POST)
if form.is_valid():
action_code = form.cleaned_data['action_code']
form.save()
return render(request, 'action_code_form.html', {'form': form})
If you need to override __init__() in your model form, then you should add self as a second parameter to super() (there is no need to keep the following two lines if you do not have any specific behavior that you want to add to your form):
def __init__(self, *args, **kwargs):
super(ActionCodeForm, self).__init__(*args, **kwargs)
Related
I’m trying to create a dashboard for the staff users to fill in and edit some information regarding their users. The form works and saves successfully, but when I submit it for a second time, it creates a new object. It won’t replace the previous:
This is my views.py file:
class ScientificInfoView(FormMixin, DetailView):
model = ScientificInfo
template_name = 'reg/scientific-info.html'
form_class = ScientificInfoForm
def get_success_url(self):
return reverse('scientific-info', kwargs={'pk': self.object.pk})
def get_context_date(self, **kwargs):
context = super(ScientificInfoView, self).get_context_data(**kwargs)
context['form'] = ScientificInfoForm()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super(ScientificInfoView, self).form_valid(form)
And my template:
<form method="POST" enctype="multipart/form-data" action="{% url 'scientific-info' pk=object.id %}">
{% csrf_token %}
{{form}}
<button type="submit">submit</button>
</form>
File urls.py:
path('surveys/scientific/<pk>', login_required(views.ScientificInfoView.as_view()), name='scientific-info')
I’m pretty sure that the action part in my form is causing the issue, but how can I solve it?
Use this:
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("scientific-info", kwargs={"pk": pk})
Or
class ScientificInfoView(FormMixin, DetailView):
model = ScientificInfo
template_name = 'reg/scientific-info.html'
form_class = ScientificInfoForm
def get_success_url(self):
return reverse("scientific-info", args=[pk]) # You can replace pk
I have a problem with saving a form in my CreateView, I found
this solution and it worked for me:
class ScheduleDocumentView(CreateView):
def post(self, request, pk, *args, **kwargs):
form = ScheduleDocumentForm(request.POST, request.FILES)
if form.is_valid():
form.instance.relates_to = Schedule.objects.get(pk=pk)
form.save()
return redirect('planning:schedule-detail', pk=pk)
However my goal is to save a form using form_valid() and get_success_url() without a template in CreateView. I tried something like this(doesn't work):
class ScheduleDocumentView(CreateView):
model = ScheduleDocument
form_class = ScheduleDocumentForm
def form_valid(self, form):
form.instance.relates_to = Schedule.objects.get(pk=pk)
form.save()
return redirect('planning:schedule-detail', pk=pk)
def get_success_url(self):
return reverse('planning:schedule-detail', kwargs={'pk': pk})
It requires a template, is there any other way to handle my post request in DetailView, process it in separate CreateView and redirect it to my DetailView page?
Here's my template for DetailView:
<form enctype="multipart/form-data" action="{% url 'planning:upload-document' schedule.pk %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="button button--secondary">Attach</button>
Urls:
path('schedules/<int:pk>/', ScheduleDetailView.as_view(), name='schedule-detail'),
path('schedules/<int:pk>/upload-document/', ScheduleDocumentView.as_view(), name='upload-document'),
I came across this solution:
class ScheduleDocumentView(CreateView):
model = ScheduleDocument
form_class = ScheduleDocumentForm
def form_valid(self, form):
form.instance.schedule = Schedule.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('planning:schedule-detail', kwargs={'pk': self.kwargs['pk']})
template_name is required Django Docs:
The full name of a template to use as defined by a string. Not
defining a template_name will raise a
django.core.exceptions.ImproperlyConfigured exception.
Or in your case Django would cause the default template_name to be 'yourapp/scheduledocument_create_form.html'.
Therefore you get the error TemplateDoesNotExist.
You can get the pk value from self.kwargs(Django Docs).
You can simple create the blank.html template.
class ScheduleDocumentView(CreateView):
http_method_names = ['post']
template_name = 'blank.html' # or use this 'schedule_detail.html'
model = ScheduleDocument
form_class = ScheduleDocumentForm
def form_valid(self, form):
form.instance.relates_to = Schedule.objects.get(pk=self.kwargs.get("pk"))
return super().form_valid(form)
def get_success_url(self):
return reverse('planning:schedule-detail', kwargs={'pk': self.kwargs.get("pk")})
Or use A simple view:
def create_schedule_document(request, pk):
if request.method == 'POST':
form = ScheduleDocumentForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False)
obj.relates_to = Schedule.objects.get(pk=pk)
obj.save()
else:
form = ApplyAnonymousForm()
return redirect('planning:schedule-detail', pk=pk)
I have two models Chapter and ChapterQuestion.
I'm using formset to create multiple ChapterQuestion records while creating Chapter record and it's working fine.
But, when I edit the form, it does not populate formset values.
I'm using UpdateView to update the record
class EditChapter(UpdateView):
model = Chapter
form_class = ChapterForm
template_name = 'courses/chapter/edit_chapter.html'
def get_context_data(self, **kwargs):
context = super(EditChapter, self).get_context_data(**kwargs)
course = Course.objects.get(pk=self.kwargs['course_id'])
if course is None:
messages.error(self.request, 'Course not found')
return reverse('course:list')
context['course'] = course
if self.request.POST:
context['chapter_questions'] = ChapterQuestionFormSet(self.request.POST)
else:
context['chapter_questions'] = ChapterQuestionFormSet()
return context
def form_valid(self, form):
context = self.get_context_data()
chapter_questions = context['chapter_questions']
with transaction.atomic():
self.object = form.save()
if chapter_questions.is_valid():
chapter_questions.instance = self.object
# chapter_questions.instance.created_by = self.request.user
chapter_questions.save()
return super(EditChapter, self).form_valid(form)
def get_success_url(self):
return reverse('course:detail', kwargs={'pk': self.kwargs['course_id']})
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(self.__class__, self).dispatch(request, *args, **kwargs)
The urls.py contains
path('<course_id>/chapter/<uuid:pk>/edit', EditChapter.as_view(), name='edit_chapter'),
and in template, I'm using crispy form
<form method="POST" role="form" class="form">
{% csrf_token %}
<h3 class="panel-title">Chapter Detail</h3>
<label for="chapter-name">Chapter Name</label>
<input name="name"
placeholder="Chapter Name"
value="{{ chapter.name }}"
id="chapter-name">
<h3 class="panel-title">Add Question to Chapter</h3>
{{ chapter_questions|crispy }}
</form>
{{ chapter_questions|crispy }} renders the form fields but fields are empty.
forms.py
from django.forms import ModelForm, inlineformset_factory
from courses.models import Chapter, ChapterQuestion
class ChapterForm(ModelForm):
class Meta:
model = Chapter
fields = ['name']
class ChapterQuestionForm(ModelForm):
class Meta:
model = ChapterQuestion
fields = ['word', 'definition']
ChapterQuestionFormSet = inlineformset_factory(Chapter, ChapterQuestion,
form=ChapterQuestionForm, extra=2)
This renders 2 empty set of fields for ChapterQuestion.
How to populated already saved data with formset?
You need to pass instance as shown here:
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/#inline-formsets
In your case:
context['chapter_questions'] = ChapterQuestionFormSet(instance=self.object)
I have a form where I must enter e-mail, but in the template do not have space for input, only after passing the validation window appears with the introduction e-mail.
At this moment I see only send button.
This is my code:
<div class="col-lg-6">
<form action="./" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="send">
</form>
</div>
model.py
class InputEmail(models.Model):
email = models.EmailField()
def __unicode__(self):
return self.email
forms.py
from models import InputEmail
from django import forms
class EmailForm(forms.ModelForm):
class Meta:
model = InputEmail
fields = ('email',)
views.py
class Home(View):
template_name = 'index.html'
def get(self, request):
return render(request, self.template_name)
def post(self, request):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, "You send e-mail to us!")
return HttpResponseRedirect('./')
else:
form = EmailForm()
return render(request, 'index.html', {
'form': form
})
The rest - writing and reading from the database works fine.
How enter the input and give it a style?
well the problem is that the first time you arrive to the view the get method is being called (and it is okay, since this is a GET request).
But, inside that get method you are not sending your form to the template. So it can't be rendered.
instead, your get method should look like that:
def get(self, request):
form = EmailForm()
return render(request, self.template_name, {"form": form})
note that the condition in the post method is redundant. You have an "else" but you never get there since the post method is only called when it is a POST request.
Meaning your post method should look like this:
def post(self, request):
form = EmailForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('./')
return render(request, self.template_name, {
'form': form
})
I want to limit a queryset for a form based on the user sending the request. I am having some trouble getting a ModelForm to properly limit the queryset of a field when the form is submitted but invalid. The form gets redisplayed with the error text, but no longer has the queryset limited. What could be the cause here?
models.py
from django.db import models
from django.contrib.auth.models import User
class Patient(models.Model):
name = models.CharField(max_length=100)
doctor = models.ForeignKey(User)
def __unicode__(self):
return self.name
class Prescription(models.Model):
name = models.CharField(max_length=100)
patient = models.ForeignKey(Patient)
views.py
import medical.models as models
import medical.forms as forms
from django.shortcuts import render
def add_form(request):
if request.method == 'POST':
form = forms.PrescriptionForm(request.POST)
if form.is_valid():
form.save()
else:
form = forms.make_prescription_form(request.user)
return render(request, 'add_form.html', {'form': form})
forms.py
import medical.models as models
from django.forms import ModelForm, ModelChoiceField
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
def make_prescription_form(dr):
class PrescriptionForm(ModelForm):
patient = ModelChoiceField(queryset=models.Patient.objects.filter(doctor=dr))
class Meta:
model = models.Prescription
return PrescriptionForm
add_form.html
{{ request.user.first_name }}
{% if form.errors %}
<p style="color: red;">Please correct the error{{ form.errors|pluralize }} below.</p>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form }}
<br>
<input type="submit" value="Submit">
</form>
I would greatly appreciate any help with this, or suggestion on a better way to achieve the same thing! Let me know if any more files would be helpful. I'm using Django 1.3.
First off, it looks like you left off a bit - make_prescription_form returns a class, not a form instance, and you're passing the class directly to the rendering in the GET path. I am assuming that's a typo.
You're not using your make_prescription_form wrapper in the POST path. The smallest change from this implementation would be:
def add_form(request):
form_class = forms.make_prescription_form(request.user)
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
form.save()
else:
form = form_class()
return render(request, 'add_form.html', {'form': form})
As for other ways to do this - you can just set the form field's queryset directly in your view.
forms.py
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
views.py
def add_form(request):
if request.method == 'POST':
form = PrescriptionForm(request.POST)
form.fields['patient'].queryset = models.Patient.objects.filter(doctor=request.user)
if form.is_valid():
form.save()
else:
form = PrescriptionForm()
form.fields['patient'].queryset = models.Patient.objects.filter(doctor=request.user)
return render(request, 'add_form.html', {'form': form})
Or set doctor as an argument to PrescriptionForm's __init__ and update the queryset there:
forms.py
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
def __init__(self, *args, doctor=None, **kwargs):
super(PrescriptionForm, self).__init__(*args, **kwargs)
if self.doctor is not None:
self.fields['patient'] = models.Patient.objects.filter(doctor=doctor)
views.py
def add_form(request):
if request.method == 'POST':
form = PrescriptionForm(request.POST, doctor=request.user)
if form.is_valid():
form.save()
else:
form = PrescriptionForm(doctor=request.user)
return render(request, 'add_form.html', {'form': form})