I have a situation where I should first display a form to the user where the user fill in two fields and after that base on the form fields I query database and show the user a list of objects.
But the problem is I use class based views and I can't access to the cleaned data in my get method. I know that the forms must be handled in post methods not get methods so I can't process form in my get method.
Here is my code:
views.py
class IncomeTransactionReport(LoginRequiredMixin, ListView):
def post(self, request, *args, **kwargs):
# here I get form from post request
form = IncomeReportForm(request.POST)
# if form is valid redirect user to the same page to show the results based on the filled
# in form fields 'from_date' and 'to_date'
if form.is_valid():
from_date = form.cleaned_data['from_date']
to_date = form.cleaned_data['to_date']
return redirect('income_report')
# else render the same page with the form and form errors
else:
error_message = 'Please solve the error and try again'
return render(request, 'report_income.html', context={'error_message': error_message,
'form': form}, status=422)
def get(self, request, *args, **kwargs):
# here I need to access 'from_date' and 'to_date' to query the database and show the results
# in paginated pages to the user
if from_date != None and to_date != None:
incomes = Income.objects.filter(user=user,
date__gte=datetime.date.fromisoformat(from_date),
date__lte=datetime.date.fromisoformat(to_date)).all()
elif from_date != None:
incomes = Income.objects.filter(user=user,
date__gte=datetime.date.fromisoformat(from_date),
date__lte=datetime.date.fromisoformat(from_date) + \
relativedelta.relativedelta(months=+1)).all()
else:
incomes = Income.objects.filter(user=user).all()
page = request.POST.get('page', 1)
paginator = Paginator(incomes, 5)
try:
incomes = paginator.page(page)
except PageNotAnInteger:
incomes = paginator.page(1)
except EmptyPage:
incomes = paginator.page(paginator.num_pages)
message = 'This is your requested list of incomes'
# here I return the results
return render(request, 'report_income.html', {'message': message, 'incomes': incomes})
I you need more information please let me know to post it here.
To anwser your question I'll just describe proper form processing in django. But for the love of god please don't post such questions before reading the documentation that perfectly explayins everything here
This is the example of how to proccess a view that has a form in it:
SomeClassBasedView(TemplateView):
template_name = 'some_template.html'
def get(self, request):
# some custom get processing
form = SomeForm() # inhereting form.Form or models.ModelForm
context = { 'form': form,
# other context
}
return render(request, self.template, context)
def post(self, request):
# this works like 'get data from user and populate form'
form = SomeForm(request.POST)
if form.is_valid():
# now cleaned_data is created by is_valid
# example:
user_age = form.cleaned_data['age']
# some other form proccessing
context = {
'form': SomeForm(),
# other context
}
return render(request, self.template, context)
# if there were errors in form
# we have to display same page with errors
context = {
'form': form,
# other context
}
return render(request, self.template, context)
After a deep search and thinking I found out that the best thing to do is to put form cleaned data in session and access it in other method such as get method.
Related
I create form with IntegerField. Is possibility to validate input values on it, from defined list?
This is a API form, which connect to another DB. It's not based on model in project.
My form looks like that:
from django.core.exceptions import ValidationError
def validate_users(value):
users_list = [10012, 16115, 10505]
if value not in users_list:
raise ValidationError('Wrong number')
class PribilagesForm(forms.Form):
mslk_module_choices = (('1', 'one'),
('2', 'two'),)
workerId = forms.IntegerField(label='Nr. ewidencyjny', validators=[validate_users])
moduleName = forms.ChoiceField(label='ModuĊ', choices=mslk_module_choices)
When I input value from out of range validate_users, and submit form, I got ValueError, not information about wrong insert value.
view:
class TestFormPrivilegesView(TemplateView):
template_name = "SLK_WS_app/SLK/test.html"
def get(self, request):
form = PribilagesForm()
return render(request, self.template_name, {'form':form})
def post(self,request, **kwargs):
form = PribilagesForm(request.POST)
if form.is_valid():
workerId = form.cleaned_data['workerId']
moduleName = form.cleaned_data['moduleName']
# args = {'form': form, 'workerId':workerId, 'moduleName': moduleName, }
request_data = {'workerId': workerId,
'moduleName': moduleName}
context = super().get_context_data(**kwargs)
client = zeep.Client(wsdl=ws_source_slk_web_service)
find_privileges = client.service.FindUserModulePermission(**request_data)
data_find_privileges = find_privileges['usersModulesPermissions']['UserModulePermissionData']
privileges_list = []
privileges_data = namedtuple('FindUserModulePermission', ['workerId',
'workerFullName',
'moduleName',
'modifyDate',
'deleted',
]
)
for element in data_find_privileges:
privileges_list.append(privileges_data(element['workerId'],
element['workerFullName'],
element['moduleName'],
element['modifyDate'],
element['deleted'],
)
)
context['privileges_list'] = privileges_list
context['Date_time'] = datetime.datetime.today()
context['form'] = PribilagesForm()
return render(request, self.template_name, context)
render(request, self.template_name, context={'form': PribilagesForm()})```
At the bottom of your view inside the post method you have:
render(request, self.template_name, context={'form': PribilagesForm()})
This needs to have return like so:
return render(request, self.template_name, context={'form': PribilagesForm()})
I'm currently building a website with Django and I've gotten to a point where I need to print data on the screen(ListView) and update(UpdateView) data on the same page(template). From what I've found I cant do this easily with the Django generic views so I rewrote my view as a function-based view. This current piece of code updates what I need perfectly with some changes to the HTML.
def DocPostNewView(request, pk):
context = {}
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context["form"] = form
posts = DocPost.objects.all()
return render(request, "my_app/use_template.html", context)
... And this following piece of code lists objects perfectly with some changes to the HTML.
def DocPostNewView(request, pk):
context = {}
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context["form"] = form
posts = DocPost.objects.all()
return render(request, 'my_app/use_template.html', context={'posts': posts})
I just need to combine these and the only real difference is the return command at the bottom of both(the same) functions. HOW CAN I COMBINE THE RETURNS SO I CAN LIST DATA AND UPDATE DATA ON THE SAME PAGE???
Thanks
does this work for you ?
def DocPostNewView(request, pk=None):
posts = DocPost.objects.all()
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context = {"form":form, "posts":posts}
return render(request, "my_app/use_template.html", context)
in your templates you could use "posts" to list every post,
and your can use "form" to update that specific post,
the thing is for every update you make you will always be seeing the whole list of posts, if it is that what you want to achieve
I've got a view in django where post method has 2 different types of form:
def post(self, request):
tweet_form = TweetForm(request.POST, request.FILES)
comment_form = TweetCommentForm(request.POST)
print(request.POST['form-button'])
if request.POST['form-button'] == 'add_tweet':
print(tweet_form.is_valid)
if tweet_form.is_valid():
content = tweet_form.cleaned_data['content']
image = tweet_form.cleaned_data['image']
tweet = Tweet.objects.create(
user=request.user,
content=content,
)
if image != None:
tweet.image = image
tweet.save()
return redirect("/home")
if request.POST['form-button'] == 'add_comment':
print(comment_form.is_valid)
if comment_form.is_valid():
content = comment_form.cleaned_data['body']
return redirect("/home")
ctx = {"tweet_form": tweet_form, "comment_form": comment_form}
return render(request, "home.html", ctx)
The both forms have different name, value, and id.
When i print (tweet_form.is_valid()) or (comment_form.is_valid()) i receive sth like:
<bound method BaseForm.is_valid of <TweetForm bound=True, valid=Unknown, fields=(content;image)>>
Respectively for both forms.
Can i somehow reorganize view to deal with both of forms or i have to have another View?
Regards
You are trying to print the method instead of evaluating the method.
Try:
print(comment_form.is_valid())
print(tweet_form.is_valid())
At my django application users are able to buy specific posted elements. To later-on display a content_preview or the full content of a post i created a "helper_model" -> Post_Paid_Sell_Tut to check if a user has paid for the post or not. My target is that i can later on display the peview or full content accordingly to the paid or unpaid status but i dont clearly understand how to get those two models in relations at my views.py
i created the following two models for this:
now i want to understand how i can bring those two models in relations at my views.py.
In the end the user will see a form with only a Buy button and some Text. after he hits the button the status should get changed from unpaid to paid for the paying user.
Do i really need a Form at this point?
and if so, how does it have to look like?
Thanks for help in adavance :)
First of all, you should change your status choices a little bit.
STATUSES = (
(0, 'unpaid'),
(1, 'paid')
)
class Post_Paid_Sell_Tut(models.Model):
paying_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_sell_tut = models.ForeignKey(Post_Sell_Tut, on_delete=models.CASCADE)
STATUS = models.IntegerField(choices=STATUSES, default=0)
This will ease later database searches and will clarify the issues. Then if you are just updating the status then there is no need for any form.
def post_sell_tut_buy(request, pk):
post = get_object_or_404(Post_Sell_Tut, pk=pk)
if request.user == post.author:
messages.error(request, 'You cant buy your own Post!')
return redirect('post_sell_tut_detail', pk=pk)
else:
template = 'Post_Sell_Tut/post_sell_tut_buy.html'
post.STATUS = 1 # You are updating the STATUS
post.save() # you are writing the change to database
context = {
'post': post
}
return render(request, template, context)
In addition by passing post instance to the template you are desired to view the informations you should follow this:
def post_sell_tut_buy(request, pk):
post = Post_Paid_Sell_Tut.objects.select_related('post_sell_tut').filter(pk=pk)
if request.user == post.author:
messages.error(request, 'You cant buy your own Post!')
return redirect('post_sell_tut_detail', pk=pk)
else:
template = 'Post_Sell_Tut/post_sell_tut_buy.html'
post.STATUS = 1 # You are updating the STATUS
post.save() # you are writing the change to database
context = {
'post': post
}
return render(request, template, context)
I added select_related because when you try to fetch information by the foreign key in from post it will not cause an extra DB request.
UPDATE
def post_sell_tut_buy(request, pk):
post = Post_Sell_Tut.objects.select_related('author).filter(pk=pk)
if request.user == post.author:
messages.error(request, 'You cannot buy POst(s) you created by your own!')
return redirect('post_sell_tut_detail', pk=post.pk)
else:
template = 'Post_Sell_Tut/post_sell_tut_buy.html'
context = {
'post': post,
}
return render(request, template, context)
You cannot access STATUS from Post_Sell_Tut model. It does not have that field.
def post_sell_tut_buy_exec(request, pk):
post_sell_tut = get_object_or_404(Post_Sell_Tut, pk=pk)
post_paid_sell_tut = Post_Paid_Sell_Tut.objects.filter(post_sell_tut=post_sell_tut)
if request.user == post.author:
messages.error(request, 'You cannot buy Post(s) you created by your own!')
return redirect('post_sell_tut_detail', pk=post.pk)
else:
template = 'Post_Sell_Tut/post_sell_tut_buy.html'
post_paid_sell_tut.STATUS = 1
post_paid_sell_tut.save()
context = {
'post': post
}
return render(request, template, context)
just create a "Post_Paid_Sell_Tut" object when a user requests to buy a post
def post_sell_tut_buy(request, pk):
post = get_object_or_404(Post_Sell_Tut, pk=pk)
if request.user == post.author:
messages.error(request, 'You cant buy your own Post!')
return redirect('post_sell_tut_detail', pk=post.pk)
else:
try:
# throws an exception if not bought yet
Post_Paid_Sell_Tut.get(paying_user=request.user, post_sell_tut=post)
messages.success(request, 'you allready have this post!')
# ... other stuff
except Post_Paid_Sell_Tut.DoesNotExist:
# create Post_Paid_Sell_Tut object indicating that the user bought the post
ppst = Post_Paid_Sell_Tut(paying_user=request.user,
post_sell_tut=post, status="paid")
ppst.save()
messages.success(request, 'post is yours :)!')
# ... other stuff
I am creating a forum where users can edit topics. When a user changes a topics title the slug for the topic is changed. When I post the changes to the topic the slug correcting middleware triggers and redirects me to back to the same editing page instead of letting me redirect to the topic itself. It seems django processes the view first and then calls post with the old url causing my changes to be saved but me not ending up in the right place. For example if I hit post without changing anything I am redirected where I should go. How do I fix this?
# middleware.py
class TopicSlugMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if (request.resolver_match and 'topic_slug' in request.resolver_match.kwargs):
try:
topic_slug = request.resolver_match.kwargs['topic_slug']
topic_pk = int(topic_slug.split('-')[0])
topic = Topic.objects.get(pk=topic_pk)
if (topic.slug != topic_slug):
return redirect(reverse(request.resolver_match.url_name, kwargs={'topic_slug': topic.slug}))
except:
raise Http404('The topic you are looking for does not exist')
return response
# views.py
class EditTopicView(View):
template_name = 'forums/edit_topic.html'
def get(self, request, topic_slug):
topic = Topic.objects.get(slug=topic_slug)
if (request.user.profile.role.can_modify_topics or request.user == topic.started_by):
if (request.user.profile.role.can_modify_topics):
form = EditMoveTopicForm(instance=topic)
else:
form = NewTopicForm(instance=topic)
return render(request, self.template_name, {'topic': topic, 'form': form})
else:
raise PermissionDenied('You do not have permission to modify this topic')
def post(self, request, topic_slug):
topic = Topic.objects.get(slug=topic_slug)
if (request.user.profile.role.can_modify_topics or request.user == topic.started_by):
if (request.user.profile.role.can_modify_topics):
form = EditMoveTopicForm(request.POST)
else:
form = NewTopicForm(request.POST)
if (form.is_valid()):
topic.title = form.cleaned_data['title']
topic.tags = form.cleaned_data['tags']
topic.save()
return redirect('/topics/' + topic.slug)
else:
return render(request, self.template_name, {'topic': topic, 'form': form})
else:
raise PermissionDenied('You do not have permission to modify this topic')