Form not being submitted - python

It was all fine just an hour ago but now when I try to add data to form and save it is running the exception part
Before the post method was called and the form used to save and redirect
But I cannot figure out why the exception part is running.
I haven't change anything
In console it is giving "POST /forum/topics/9/reply HTTP/1.1" 200 523
class NewPostView(generic.CreateView):
form_class = NewPostForm
template_name = 'forum/new_post.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if 'pk' not in context:
context['pk'] = self.kwargs['pk']
return context
def post(self, request, *args, **kwargs):
topic = get_object_or_404(Topic, pk=self.kwargs['pk'])
form = self.get_form()
try:
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = User.objects.get(pk=request.user.pk)
post.save()
return redirect('topic-detail', pk=topic.pk)
except Exception as e:
messages.warning(request, "Failed To Create. Error: {}".format(e))
messages.warning(request, "Failed To Create Check Errors")
args = {'form': form, 'pk': self.kwargs['pk']}
#print(args)
return render(request, self.template_name, args)
Edit:Now I can see User matching query does not exist. error
Edit2 : Thank you all for the response. I found the error that I had made, I was actually using django.contrib.auth.user in the login while I was using my own User model.
When I change created_by = models.ForeignKey("auth.User"), in the model then it worked fine.

My guess is that you are no longer logged into the application, so request.user.pk is None.
The easiest way to ensure only a logged in user can access your view is to add the LoginRequiredMixin:
from django.contrib.auth.mixins import LoginRequiredMixin
class NewPostView(LoginRequiredMixin, generic.CreateView):

I have changed your views.py
class NewPostView(View):
.....add your template_name, form_class and get function....
def post(self, request, *args, **kwargs):
topic = get_object_or_404(Topic, pk=self.kwargs['pk']) # here you can directly add pk=pk and def post(self, request, pk)
form = self.form_class(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = self.request.user
post.save()
return redirect('topic-detail', pk=topic.pk)
else:
messages.error(request, "Error Message")
return render(request, self.template_name, {'form':form})
For not logged in user, you can follow Daniel's answer and/or in your template you can add,
{% if user.is_authenticated %}
html
{% else %}
html
{% endif %}

Related

In django, how can I ensure that a specific user is accessing a view?

I would like to be able to check if the user accessing a page is a specific user.
For instance, when a user accesses the "Edit Post" page on my blog app, I want to ensure that the user is the author of the post.
Currently, I check that the user accessing '/Blog/Edit//' has the blog.change_post permission.
However, if a second user (who also has that permission) were to enter the URL to change someone else's post, they would pass that permission check and be able to edit someone else's post.
What I want is a #user_passes_test function that check's the user object accessing the view against the author attribute of the post.
#/Blog/urls.py
urlpatterns = [
...
path('Edit/<int:pk>', views.BlogEdit, name='BlogEdit'),
...
]
#/Blog/views.py
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('/Blog', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'Blog/Edit.html', {'form': form})
You can add an extra filter to your get_object_or_404:
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk, author=request.user)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
form.save()
return redirect('/Blog', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'Blog/Edit.html', {'form': form})
Here author is the hypothetical ForeignKey from Post to the user model. It is possible that the name is different, but the idea is still the same.
This thus means that in case the pk is the pk of a Blog for which request.user is not the author, then we will have a 404 response.
The advantage of filtering here, is that we use a single query for the filtering. We will not (lazily) load the author to check if it is the same as the logged in user.
Note: post = form.save(commit=False) and post.save() are equivalent to post = form.save() (so with commit=True).
In a class based view change the get_queryset method to filter by current user.
class MyView(LoginRequiredMixin, UpdateView):
...
def get_queryset(self):
"""
Only the current logged in user can edit ...
"""
return super().get_queryset().filter(created_by=self.request.user)
I figured it out from This Post in the end. What urbanspaceman suggests works for class based views, but for function based views you can do:
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk)
if post.author != request.user:
raise Http404("You are not allowed to edit this Post")
# ... Rest of view here ... #

Get current logged in user in Django forms

I need to get the current logged in user in Django form. I need it to get the domain from his email and accordingly fetch the list of other users with similar domain. Here is my code so far:
forms.py
class AuthUserCheckbox(forms.Form):
choice = forms.MultipleChoiceField(choices=[], widget=forms.CheckboxSelectMultiple, required=True)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
self.user_email = self.user.email.split('#')[1]
super(AuthUserCheckbox, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.MultipleChoiceField(choices=[(i.id, i.email)
for i in User.objects.all()
if not i.is_active and
self.user_email in i.email
])
views.py
#login_required
def auth_users(request):
form = auth_users(data=request.POST, user=request.user)
return render(request, 'todoapp/auth_users.html', context={'form': AuthUserCheckbox()})
You need to pass the request's user into your form constructor and then place that form in context. Your view needs to look something like:
#login_required
def auth_users(request):
form = AuthUserCheckbox(request.POST, user=request.user)
return render(request, 'todoapp/auth_users.html', context={'form': form})
Of course, the above is an incomplete view, because you shouldn't just grab POST like that, but that is not the question.

how to insert records in relational model in relation to the first model(parent-model) database entities

Here, I have two separate models 'User' model and 'Event' model. Event model is related to User mode. Now what i want to do is, when a user fills the event form and submit, then that event record should be created in relation to that user only.
views.py
class EventCreationForm(View):
template_eventcreationform = 'eventcreationform.html'
form_class = EventForm
def get(self, request):
form = self.form_class(None)
return render(request, self.template_eventcreationform, {'form': form})
def post(self, request):
# data is here
form = self.form_class(request.POST)
if form.is_valid():
event = form.save(commit=False)
user = User.objects.get(
#######################################################
##### currently logged user #####
### what should i put here to get current user? ###
#######################################################
)
form.user = user.email
event_name = form.cleaned_data.get('event_name')
event_date_time = form.cleaned_data.get('event_date_time')
event_address = form.cleaned_data.get('address')
event.save()
message = 'Event added'
return render(request, 'base.html', {'message': message})
else:
message = 'Invalid form data, try again'
form = self.form_class(None)
return render(request, self.template_eventcreationform, {'message': message, 'form': form})
Firstly, you might want to use LoginRequiredMixin so that only logged-in users can access the view.
from django.contrib.auth.mixins import LoginRequiredMixin
class EventCreationForm(LoginRequiredMixin, View):
You can access the logged in user with self.request.user.
if form.is_valid():
event = form.save(commit=False)
event.user = self.request.user
event.save()
message = 'Event added'
return redirect('/success-url/')
Note that it's a good idea to redirect after the event has been saved, to prevent duplicate form submissions.
Note that you are duplicating lots of the functionality of FormView or CreateView. If you use these you won't have to write as much code.
class EventCreationForm(LoginRequiredMixin, CreateView):
template_name = 'eventcreationform.html'
form_class = EventForm
success_url = '/success-url/' # form_valid() will redirect here
def form_valid(self, form):
form.instance.user = self.request.user
return super(EventCreationForm, self).form_valid(form)

Django Many To Many Management with inline formset

I can't seem to find a good way to solve this situation.
I have two models Office and People with a many to many relationship through a Contact model with additional fields.
Now in my views (CreateView and UpdateView), I am using inline formset to manage the relationship.
My problem here is with UpdateView, how do I update the many to many relationship? I can add new items. But how to delete existing ones? The formset generates a checkbox DELETE but I get lost in the code. How to use it?
One way could be to delete all the corresponding rows in the through model and recreates new ones with data submitted from the form but I believe there should be a more efficient way to do it.
Can someone help?
Here's my current code:
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = OfficeContactInline(request.POST, instance=self.object)
if form.is_valid() and formset.is_valid():
self.object = form.save()
contacts = formset.save(commit=False)
for contact in contacts:
contact.office = self.object
contact.save()
formset.save_m2m()
return HttpResponseRedirect(self.get_success_url())
else:
return render(request, self.template_name, self.get_context_data(form=form, formset=formset))
I finally found a solution to my problem. There actually is a change in behavior with Django 1.7: formset.save(commit=False) no longer deletes deleted items (checkbox checked).
You therefore have to use formset.deleted_objects to do it: working code below
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = OfficeContactInline(request.POST, instance=self.object)
if form.is_valid() and formset.is_valid():
self.object = form.save()
contacts = formset.save(commit=False)
# New with Django 1.7
for del_contact in formset.deleted_objects:
del_contact.delete()
for contact in contacts:
contact.office = self.object
contact.save()
formset.save_m2m()
return HttpResponseRedirect(self.get_success_url())
else:
return render(request, self.template_name, self.get_context_data(form=form, formset=formset))
This is documented here: https://docs.djangoproject.com/en/1.7/topics/forms/formsets/#django.forms.formsets.BaseFormSet.can_delete

Class based view extending UpdateView not saving form correctly

Im trying to save a form using UpdateView in Django 1.3 and seemed to have run into a problem. When I am saving the form, it POST's to the current URL and the success url is the same url.
When saving the form, the data seems to be changed because all the fields on the page are updated, but when I refresh, everything seems to revert.
The form is a Model Form and here is my view:
class UserProfileView(UpdateView):
context_object_name = 'profile'
def get_template_names(self):
return ['webapp/user_profile.html']
def get_queryset(self):
pk = self.kwargs.get('pk', None)
if pk is not None:
user = User.objects.get(pk=pk)
else:
raise AttributeError(u"Could not locate user with pk %s"
% pk)
if user.contributor_profile.all():
queryset = Contributor.objects.filter(user__pk=pk)
else:
queryset = Member.objects.filter(user__pk=pk)
return queryset
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
return queryset.get()
I dont see what could be going wrong, seeing as Django saves the form through the UpdateView class and the Mixin's it extends. Has anyone run into this problem before?
Figured out the solution. The problem was happening because there was an error in the form that wasnt being reported. This seems to occur with hidden fields that need to be set in some way in order for the form to be valid.
The solution is pretty simple. You just need to override the post function and account for any hidden fields:
def post(self, request, *args, **kwargs):
pk = self.kwargs.get('pk', None)
if pk is not None:
user = User.objects.get(pk=pk)
else:
raise AttributeError(u"Could not locate user with pk %s"
% pk)
if user.contributor_profile.all():
contributor = Contributor.objects.get(user=user)
form = ContributorForm(request.POST, instance=contributor)
else:
member = Member.objects.get(user=user)
form = MemberForm(request.POST, instance=member)
if form.is_valid():
self.object = form.save()
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(self.get_context_data(form=form))

Categories