I am trying to set the initial value of a form field.
I have approached it the following ways:
forms.py:
class FilterGigsForm(forms.Form):
field = forms.IntegerField(label="", required=False, initial=5)
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
kwargs.update(initial={
'field': 5,
})
super().__init__(*args, **kwargs)
self.fields['field'].initial = 5
views.py
class HomePage(FormMixin, TemplateView):
template_name = 'index.html'
model = MyModel
form_class = MyForm
def get(self, request, *args, **kwargs):
form = MyForm(self.request.GET, initial={'field': 5,})
...
return render(request, 'index.html', {"form": form, })
I have loaded the form field into my template as follows:
<div class="styling">
{{ form.field|as_crispy_field }}
</div>
However, none of these approaches are working and the initial value is not showing when I load/reload the page.
form = MyForm(initial={'field': 5,})
Related
I have a Django view and I want to send the request data to a form.
class PostDetailView(DetailView):
model = Post
form = CommentForm
def get_form_kwargs(self):
kwargs = super(PostDetailView, self).get_form_kwargs()
kwargs['user'] = self.request.user.username
return kwargs
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
post = self.get_object()
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect(reverse("post-detail", args=[post.pk]))
from django import forms
from .models import Comment
from djrichtextfield.widgets import RichTextWidget
class CommentForm(forms.ModelForm):
user = forms.ChoiceField(required=True)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(CommentForm, self).__init__(*args, **kwargs)
self.fields['user'].choices = self.user
content = forms.CharField(widget=RichTextWidget(attrs={
'class': 'md-textarea form-control',
'placeholder': 'Comment here ...',
'rows': '4',
}))
class Meta:
model = Comment
fields = ('author', 'content',)
<form method="POST" href="{% url 'post-detail' post.id %}">
{% csrf_token %}
<div class="form-group">
{{form | safe}}
</div>
<button class="btn btn-primary btn-block" type="submit">Comment</button>
</form>
urlpatterns = [
path('post/<int:pk>/', PostDetailView.as_view(), name="post-detail"),
]
According to the docs, get_form_kwargs allows key-value pairs to be set in kwargs. The kwargs is then passed into the form. The form's init function should then be able to pick up the user value from kwargs.
However, self.user returns None, and debugging showed that get_form_kwargs did not run at all.
I have two questions: how do functions in view classes get executed? And what is the correct method to pass data from a view to a form?
EDIT
I have refactored the comment feature into another view.
class AddCommentView(UpdateView):
model = Post
form = CommentForm
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
post = self.get_object()
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect(reverse("post-detail", args=[post.pk]))
def get_form_kwargs(self):
kwargs = super(PostDetailView, self).get_form_kwargs()
kwargs.pop('instance', None)
kwargs['user'] = self.request.user.username
return kwargs
<form method="POST" href="{% url 'add-comment' post.id %}">
{% csrf_token %}
<div class="form-group">
{{form | safe}}
</div>
<button class="btn btn-primary btn-block" type="submit">Comment</button>
</form>
urlpatterns = [
path('post/<int:pk>/', AddCommentView.as_view(), name="add-comment")
]
However UpdateView cannot handle POST requests (405).
It's not explicitly described in the docs but get_form_kwargs is only triggered with a CreateView or an UpdateView.
In your case you can use UpdateView, and then use form_valid to do your post process. But note that we need to delete kwargs['instance'], because by default this view will think we are working with a Post object when in fact it's a Comment:
Try with this:
class PostDetailView(UpdateView):
model = Post
form = CommentForm
def get_form_kwargs(self):
kwargs = super(PostDetailView, self).get_form_kwargs()
# Remove the post object as instance,
# since we are working with a comment
kwargs.pop('instance', None)
kwargs['user'] = self.request.user.username
return kwargs
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.post = self.get_object() # returns the post
instance.save()
return redirect(reverse("post-detail", args=[post.pk]))
or if you don't want to use UpdateView (not recommended), you can just explicitly call get_form_kwargs when you build your form. You cannot call super().get_form_kwargs() though, since as discussed the parent class doesn't have this method:
class PostDetailView(DetailView):
model = Post
form = CommentForm
def get_form_kwargs(self):
kwargs = {'user': self.request.user.username}
return kwargs
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST, **self.get_form_kwargs())
if form.is_valid():
post = self.get_object()
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect(reverse("post-detail", args=[post.pk]))
Lastly, do consider renaming this class, since we are working with adding a comment to post, and not really about "post detail", so something like PostAddCommentView?
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 want to display DetailView and independent form to send API request to the other website server. I made views.py but only i get is empty page. I'm trying to figure out how to adjust it for over past fiew days and still don't have any clue how to do this. Hope you will help me with this
views.py
class DetailPostDisplay(DetailView):
model = EveryPost
template_name = 'post/detailpost.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = DictForm()
return context
class DictWindowForm(SingleObjectMixin, FormView):
template_name = 'post/detailpost.html'
form_class = DictForm
model = EveryPost
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('detailpost', kwargs={'slug': self.object.slug})
class DetailPostList(View):
def get(self, request, *args, **kwargs):
view = DetailPostDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = DictWindowForm.as_view()
return view(request, *args, **kwargs)
HTML
I'm not sure whether action should be empty or include url DetailPostDisplay(require to pass slug, which i don't have how to get)
<form method="POST" action="">
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-dark float-right mt-2" value="Tłumacz">
</form>
urls.py
from django.urls import path
from . import views
from .views import PostListPl, PostListRu, DetailPostDisplay
urlpatterns = [
path('', PostListPl.as_view(), name='index_pl'),
path('ru/', PostListRu.as_view(), name='index_ru'),
path('about/', views.about, name='about'),
path('<slug:slug>/', DetailPostDisplay.as_view(), name='detailpost'),
]
For the future generations, i mixed and overthinked it. If you want to just put form into DetailView, create def post and put logic there. Code below:
views.py
class DetailPostDisplay(DetailView):
model = EveryPost
template_name = 'post/detailpost.html'
def get_context_data(self, **kwargs):
context = super(DetailPostDisplay, self).get_context_data(**kwargs)
context['form'] = DictForm
return context
def post(self, request, *args, **kwargs):
form = DictForm(request.POST)
if form.is_valid():
self.object = self.get_object()
And later code to pass variables into template from form
context = super(DetailPostDisplay, self).get_context_data(**kwargs)
context['form'] = DictForm
context['word'] = request.POST.get('word')
return self.render_to_response(context=context)
I have a DemandDetailView(DetailView) and BidCreationView(CreateView).
On DemandDetailView page, there is a form (for creating Bids) which posts data to BidCreationView.
I can't figure out what to do in case form is invalid. I would like to render DemandDetailView again with form errors and preserve corresponding URL.
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
self.bid_creation_form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
class BidCreationView(CreateView):
http_method_names = ['post']
model = Bid
form_class = BidCreationForm
def get_success_url(self):
return reverse_lazy("demands:demands")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.instance.demand_id = self.kwargs.pop('demand_id')
return super().form_valid(form)
Do you have any ideas? My only idea is to use session which isn't probably the best way.
You could use is_valid() method from Form Objects. Something like:
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
if form.is_valid():
self.bid_creation_form = form
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
Option #2 (Personal Choice):
forms.py
from django import forms
from .models import Bid
class BidCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BidCreationForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs = {"class": "form-control"}
class Meta:
model = Bid
fields = ('user', 'demands', 'amount', 'transaction')
Take a look at the Meta class within the Form. Its explicitly calling the Bid Model and the fields attribute is referring Bid fields from the Model Instance. Now you could call this form in any view without calling another view. If you want to add logic to this form, like calculating total amount or something like that then should do it within the form also. Write code once, Dont Repeat yourself.
I have a DetailView which displays a Post. I now want to add the ability to create a Comment for a Post. For that I need a CommentForm, within the DetailView, so that I can create comments while being on the same page with a Post.
Is this possible, or should I be looking for another approach, like doing the form handling 'manually'?
class Comment(models.Model):
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
author_name = models.CharField(max_length=255)
parent_post = models.ForeignKey('Post',related_name='comments')
class PostDetailView(BlogMixin,DetailView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Post
#Add some code for the CommentForm here?
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ("parent_post","created_at")
def create_view(request, **kwargs):
if request.method == "POST":
parent_fk = request.args['parent_fk'] #Im hoping to find how this will work soon
form = CommentForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.parent_post = parent_fk
new_comment.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
** Alternative **
I have been trying to apply the solution - A better alternative - but I get
Exception Value: __init__() takes exactly 1 argument (3 given)
Exception Location: .../sitepackages/django/core/handlers/base.py in get_response, line 112
and have not been able to trace it yet.
class PostView(BlogMixin,DetailView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Post
def get_context_data(self, **kwargs):
context = super(PostView, self).get_context_data(**kwargs)
context['form'] = CommentForm()
return context
class PostDetailView(View):
def get(self, request, *args, **kwargs):
view = PostView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = PostComment.as_view()
return view(request, *args, **kwargs)
class PostComment( SingleObjectMixin , FormView):
template_name = 'post.html'
form_class = CommentForm
model = Post
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(PostComment, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.pk})
class BlogMixin(object):
"""
Basic mixin for all the views. Update the context with additional
information that is required across the whole site, typically
to render base.html properly
"""
def get_context_data(self, *args, **kwargs):
context = super(BlogMixin, self).get_context_data(*args, **kwargs)
blog = Blog.get_unique()
context.update({
'blog': blog,
'active_user': users.get_current_user(),
'is_admin': users.is_current_user_admin()
})
return context
urls.py:
url(r'^post/(?P[\d]+)/$', views.PostDetailView., name="post-detail"),
If you want to use your first method, you can make the FK a hidden field. In your view, you can save the FK before committing the comment to the database. Like this:
if form.is_valid():
comment = form.save(commit=False)
comment.parent_post = parent_post
comment.save()
Edit: If you want to fetch the comments, then you can use filter by post to get a QuerySet of the comments.
Why dont you send your form in the context in detail view:
class YourDetailView(DetailView):
#Your stuff here
def get_context_date(self, **kwargs):
context = super(YOurDetailView, self).get_context_data(**kwargs)
context['form'] = YourForm
return context
PS. Look for the parameters in get_context_date..
In the end I could not make it work with the redirect, but the following is working:
class PostDetailView(BlogMixin,CreateView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Comment
fields = ['body','author_name']
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['post'] = Post.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
# self.object = form.save()
obj = form.save(commit=False)
obj.parent_post = Post.objects.get(pk=self.kwargs['pk'])
obj.save()
return redirect('post-detail', self.kwargs['pk'])