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)
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 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,})
I am getting error when go to the url: /result_list.html. But there is indeed queryset in the views.py, so what could be other reasons? Thanks in advance. The function is based on the form submitted by user, then filter the database and show the form entry and database result in the result_list.html.
Traceback
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\list.py" in get
159. self.object_list = self.get_queryset()
File "C:\Users\user\Desktop\SCOR\result\views.py" in get_queryset
70. return super(ResultView,self).get_queryset()
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\list.py" in get_queryset
44. 'cls': self.class.name
Exception Type: ImproperlyConfigured at /result_list/
Exception Value: ResultView is missing a QuerySet. Define ResultView.model, ResultView.queryset, or override ResultView.get_queryset().
Here below is the snippets:
url
from result.views import ResultView,InputFormView
from django.views.generic import TemplateView,FormView,ListView
urlpatterns = patterns('',
url(r'^result_list/$',ResultView.as_view(),name='result'),
url(r'^input/$',InputFormView.as_view(),name='input'),
)
views.py
from result.forms import InputForm
from result.models import Result,Input
from django.views.generic.list import ListView
from django.views.generic import FormView
....
#csrf_exempt
class InputFormView(FormView):
template_name = 'inputform.html'
form = InputForm
def get_success_url(self): /*redirect to result page with submitted form information*/
return ''.join(
[
reverse('dupont'),
'?company=',self.request.POST.get('company'),
'?region=',self.request.POST.get('region')
]
)
class ResultView(ListView):
context_object_name = 'result_list'
template_name = 'result_list.html'
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
return context
def get_queryset(self):
if self.request.method == 'POST':
form = InputForm(self.request.POST)
if form.is_valid():
company = form.cleaned_data['company']
region = form.cleaned_data['region']
/---Based on form entry, do the filter on the database-----/
queryset=Result.objects.filter(region=region,company=company)
sales=Result.objects.filter(queryset).aggregate(Sum('sales'))
employee=Result.objects.filter(queryset).aggregate(Sum('employee'))
departments=Result.objects.filter(queryset).aggregate(Sum('departments'))
form.save()
return render(request,'result_list.html',{'company':company},{'region':region},{'employee':employee},{'sales':sales},{'departments':departments})
else:
print form.errors
else:
form=InputForm()
return super(ResultView,self).get_queryset()
result_list.html
<div class="basicinfo"> <!--Entry Form information submitted by user-->
<table border="1" cellpadding="1">
<tr>
<td align="left">Company</td>
<td>{{company}}</td>
</tr>
<tr>
<td align="left">Region</td>
<td>{{region}}</td>
</tr>
</table>
<!--Showing the filtered result in database-->
<td><table border="0" cellspacing="10" cellpadding="10">
<tr><b>Sales</b></tr>
<td bgcolor="#F0F0F0"> {{sales}}</td>
</tr>
<tr><b>Employee</b></tr>
<tr>
<td bgcolor="#F0F0F0"> {{employee}}</td>
</tr>
<tr><b>Departments</b></tr>
<td bgcolor="#F0F0F0"> {{departments}}</td>
</td></table>
Version 2.0 views.py as per solarissmoke' and pythad's suggestion
class InputFormView(FormMixin,DetailView):
template_name = 'inputform.html'
form = InputForm
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)
print form.errors
def form_valid(self, form):
company = form.cleaned_data['company']
region = form.cleaned_data['region']
return super(ResultView,self).form_valid(form) 《Q1:---If here to return to ResultView or InputFormView?--->
def get_success_url(self): #Redirect to result_list.html
return reverse('result', kwargs={'pk': self.object.pk})
class ResultView(MultipleObjectMixin,DetailView):
context_object_name = 'result_list'
template_name = 'result_list.html'
<---Q2: How can I get the form data entry from InputFormView?-->
def get(self, request, *args, **kwargs):
view = InputFormView.as_view()
self.object = self.get_object(queryset=Result.objects.filter(company=company,region=region))
return super(Result, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context['InputFormView']= self.object
return context
def get_queryset(self):
<--Error!-- this sentence is where reports error as "global name company,region not defined"-->
queryset=Result.objects.filter(company=company,region=region)
sales=Result.objects.queryset.aggregate(Sum('sales'))
employee=Result.objects.queryset.aggregate(Sum('employee'))
departments=Result.objects.queryset.aggregate(Sum('departments'))
return self.object.all()
There are multiple issues with your code:
You have an if self.request.method == 'POST': block in your get_queryset method. If that evaluates to False then you fall back to the parent get_queryset method, which is what triggers the error (because the parent method needs to be given either a model or a queryset, as indicated in the error). So if you try a simple GET request to result_list/ it will fail because the ListView doesn't have a queryset.
You shouldn't be rendering templates from inside the get_queryset method. That method should return a queryset and nothing else. This will fail.
Your ResultView really shouldn't be handling the POSTed form data in the first place. You should be handling that in your FormView and then redirecting the user to the appropriate result view by using the FormViews success_url parameter as explained in the documentation on form processing.
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'])
I have an input field <input type="hidden" name="next" value="{{ next }}" />, but it wont replace {{ next }} with the value of the GET parameter. I can't find how to replace the value.
My view
class LoginView(FormView):
form_class = AuthenticationForm
redirect_field_name = REDIRECT_FIELD_NAME
template_name = 'user/login.html'
#method_decorator(csrf_protect)
#method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
return super(LoginView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
login(self.request, form.get_user())
return super(LoginView, self).form_valid(form)
def get_success_url(self):
if self.success_url:
redirect_to = self.success_url
else:
redirect_to = self.request.REQUEST.get(self.redirect_field_name, '')
if not redirect_to:
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
return redirect_to
Override method get_context_data on class LoginView.
def get_context_data(self, **kwargs):
context = super(LoginView, self).get_context_data(**kwargs)
context['next'] = self.request.REQUEST.get('next')
return context
This assumes that the GET parameter will have the next url where user should be redirected in param next. So, request should be of form localhost.com/login?next=/accounts/