passing object between forms and views - django-views

I have a problem with passing object between views and forms and back.
On first form i check token (GET) with email - if it's ok - you can go further. If not - go away :D
views.py:
def login(request):
try:
token = request.GET['token']
except:
return render(request,'error.html')
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/vote/')
else:
form = LoginForm(initial={'token': request.GET['token']})
return render(request,'login.html', context = {'form':form})
forms.py:
class LoginForm(forms.Form):
email = forms.EmailField(label='Email', max_length=254,widget=forms.TextInput(attrs={'class':'required'}))
token = forms.CharField(widget=forms.HiddenInput())
def clean(self):
cleaned_data = super().clean()
try:
voter = Person.objects.get(email__iexact=cleaned_data['email'],token__exact=cleaned_data['token'])
except Person.DoesNotExist:
raise ValidationError('Invalid email')
It works.
But now i try to go to voting form.
And I want to use voter object (which is set in LoginForm). Of course this is different form, so I have to pass it. I thought about session, but there's no request.session in form. This is in view, but there's no voter... or is it?

As always when I'm stuck for many minutes, I wrote the question and after few minutes I got excellent solution, so I want to share it with you:
I moved checking into view and use form.add_error. And I don't need token hidden field anymore:
forms.py:
class LoginForm(forms.Form):
email = forms.EmailField(label='Email', max_length=254,widget=forms.TextInput(attrs={'class':'required'}))
views.py:
def login(request):
try:
token = request.GET['token']
except:
return render(request,'error.html')
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
try:
voter = Person.objects.get(email__iexact=form.cleaned_data['email'],token__exact=token)
return HttpResponseRedirect('/vote/')
except Person.DoesNotExist:
form.add_error('email','Invalid email')
else:
form = LoginForm()
return render(request,'login.html', context = {'form':form})
And now I can pass voter into next form using request, session, whatever :D

Related

Django. Populate user name or ID when user saving a model from web pages

My UserImg Model has a user field that has editable=False.
I want this field to be automatically filled in with the user name when the user is saved from web page.
model.py
def upload_myimg_path(instance, filename):
return 'documents/{0}/{1}'.format(instance.created_by.username, filename)
class UserImg(models.Model):
user = models.ForeignKey(User, verbose_name=_('Created by'), on_delete=models.CASCADE, editable=False, null=True, blank=True)
name = models.CharField(max_length=100, default='')
image = models.ImageField(upload_to=upload_myimg_path, verbose_name=_('File'))
def __str__(self):
return str(self.user)
forms.py
class UserImgForm(forms.ModelForm):
class Meta:
model = UserImg
fields = '__all__'
views.py
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/accounts/users')
else:
return redirect('/accounts/')
else:
form = UserImgForm
return render(request, 'accounts/user_form.html', {'form': form})
Update your view function to include current logged in user and make use of #login_required decorator to ensure that only logged in users can access this view :
from django.contrib.auth.decorators import login_required
#login_required
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False) # <-- commit=False does not save to database
obj.user = request.user # <-- this allows you to specify the user for your post
obj.save()
return redirect('/accounts/users')
# if the form did not validated, stay on the same page to display errors to your user
else:
form = UserImgForm()
return render(request, 'accounts/user_form.html', {'form': form})
correct answer commit=False allows you to modify the resulting object before it is actually saved to the database. It`s works for me.
Thank you very much for your help
from django.contrib.auth.decorators import login_required
#login_required
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST, request.FILES)
if form.is_valid():
link = form.save(commit=False)
link.user = request.user
link.save()
return redirect('/accounts/users')
# if the form did not validated, stay on the same page to display errors to your user
else:
form = UserImgForm()
return render(request, 'accounts/user_form.html', {'form': form})

How to use Httprequest object in FormView?

i was converting the below function view in class based view :
but the problem was the below login code uses request object . so how to use this request in a form view .
Functional view i wanted to change to class based view :
def login(request):
if request.method == 'GET':
form = UserLoginForm()
elif request.method == 'POST':
form = UserLoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get('name')
password = form.cleaned_data.get('password')
user = User.objects.filter(name=username, password=password).first()
if user is not None:
request.session['user'] = username
return redirect('index')
else:
messages.error(request, 'Username or password no matched')
return render(request, 'products_app/login.html', {'form': form})
FormView/class based view of the above code i changed to that gives error :
class Login(FormView):
template_name = 'products_app/login.html'
form_class = UserLoginForm
success_url = reverse_lazy('index')
def form_valid(self, form):
username = form.cleaned_data.get('name')
password = form.cleaned_data.get('password')
user = User.objects.filter(name=username, password=password).first()
if user is not None:
request.session['user'] = username
else:
messages.error(request, 'Username or password no matched')
super().form_valid(form)
here the problem is ,request is not being received unlike in the functional view of above def login(request). so gives error:
module 'django.http.request' has no attribute 'session'
The problem is you are using the request module as stated in the error. What you actually want is the request instance that invoked the class. Your code should be self.request.
I've eliminated all the superfluous code to only show the parts with request.
class Login(FormView):
...
def form_valid(self, form):
...
if user is not None:
self.request.session['user'] = username
else:
messages.error(self.request, 'Username or password no matched')
...

KeyError in Django form kwargs

def user_info(request, template_name='social/retrieve_user_data.html', username=None):
user = User.objects.get(username=username)
if request.method == 'POST':
form = UserInfoForm(request.POST, user)
print(form.is_valid())
if form.is_valid():
form.save()
else:
print('not post')
form = UserInfoForm(user)
return render_to_response(template_name, RequestContext(request, {
'form': form,
}))
class UserInfoForm(forms.Form):
def __init__(self,*args,**kwargs):
self.user = kwargs.pop('user')
super(UserInfoForm,self).__init__(*args,**kwargs)
This is producing a KeyError with exception value u'user'. What is wrong here? In both cases, form is initialized with a valid value of user. Why am I getting a keyerror>
You're not passing the user as a keyword argument in either the if or the else block. It should be:
form = UserInfoForm(request.POST, user=user)
and:
form = UserInfoForm(user=user)
Only func(foo=bar)can put {"foo":bar} into kwargs dictionary! According to your code form = UserInfoForm(request.POST, user),you can use self.user = args[1] to capture user.

form not visible on browser even with my get and post methods implemented in django 10.5

class Profile(View):
"""User Profile page reachable from /user/<username> URL"""
def get(self, request, username):
params = dict()
user = User.objects.get(username=username)
tweets = Tweet.objects.filter(user=user)
params["tweets"] = tweets
params["user"] = user
return render(request, 'profile.html', params)
class PostTweet(View):
"""Tweet Post form available on page /user/<username> URL"""
def post(self, request, username):
form = TweetForm(self.request.POST)
if request.method == 'POST':
if form.is_valid():
user = User.objects.get(username=username)
tweet = TweetForm(text=form.cleaned_data['text'],
user=user,
country=form.cleaned_data['country'])
tweet.save()
words = form.cleaned_data['text'].split(" ")
for word in words:
if word[0] == "#":
hashtag,created=HashTag.objects.get_or_create(name=word[1])
hashtag.tweet.add(tweet)
return HttpResponseRedirect('/user/'+user)
else:
form = TweetForm()
return render(request, 'profile.html',{'form':form})
You must make both methods in a single view class.
So on get user gets empty form, and on post form gets processed.

How to clear form fields after a submit in Django

I've this:
def profile(request, username):
if request.method == 'POST':
if request.user.is_authenticated():
new_message = Message(author = request.user)
form = MessagesForm(request.POST, instance = new_message)
else:
form = MessagesForm(request.POST)
if form.is_valid():
form.save()
else:
to_user = User.objects.get(username = username)
form = MessagesForm(initial = {'user': to_user.pk})
return render(request, "profile.html", {
'username': username,
'form': form,
'messages': messages,
})
This form submit a message and return the same page. My problem is that after the submit I see again my field filled with my information. How to clear it after the submit?
After saving form instead of showing post dict assign the empty form
form = EmployeeForm()
if request.method == "POST":
pDict = request.POST.copy()
form = EmployeeForm(pDict) #if not valid shows error with previous post values in corresponding field
if form.is_valid():
form.save()
form = EmployeeForm() # show empty form no need to give HttpResponseRedirect()
It's standard to redirect after form submission to prevent duplicates.
Just return a redirect to your form on success.
if form.is_valid():
form.save()
return http.HttpResponseRedirect('')
after save() you can return 'form' key with MessagesForm(request.GET) value.
return render(request, "profile.html", {
'username': username,
'form': MessagesForm(request.GET),
'messages': messages,
})
Usually you can initialize the same empty form after you have saved datas:
if request.method == "POST":
rf = RegistrationForm(request.POST)
if rf.is_valid():
print 'Saving datas..'
#logic to save datas
rf = PreRegistrationForm()
return render_to_response('registration/confirmation_required.html', {'settings': settings}, context_instance=RequestContext(request))
Try using HttpResponseRedirect('/') instead of HttpResponseRedirect('') in #Karthikkumar's answer, especially if your home view is an empty path, for instance you have in your urls.py file:
urlpatterns = [path('',views.home_view),]
I had similar issues as those discussed above where HttpResponseRedirect('') directed me to a blank page. Let me know if adding the slash works for you!
You can use this:
Sometimes you can use this idea take attrs={ "autocomplete":"off"} for each inputs.
You can redirect back to the initial post
post_url = request.build_absolute_uri(post.get_absolute_url())
return HttpResponseRedirect(post_url)
please note that 'post' is an instance of the model created
I am hoping you have already defined a logic for GET methods. In the case that you have, all you can do is simply add return request.META['HTTP_REFERER'] at the end.
See what I mean:
def profile(request, username):
if request.method == 'POST':
if request.user.is_authenticated():
new_message = Message(author = request.user)
form = MessagesForm(request.POST, instance = new_message)
else:
form = MessagesForm(request.POST)
if form.is_valid():
form.save()
else:
to_user = User.objects.get(username = username)
form = MessagesForm(initial = {'user': to_user.pk})
return HttpResponseRedirect(request.META['HTTP_REFERER'])
This should work, I just tested it.
**Maybe I am late but for Django 4.x developers can use: **
from .forms import TodoForm
from django.contrib import messages
from django.http import HttpResponseRedirect
def mytodos(request):
form = TodoForm()
if request.method =='POST':
form=TodoForm(request.POST)
if form.is_valid:
form.save()
messages.success(request,'Task saved successfully')
return HttpResponseRedirect('/todo/')
mycontext={'form':form}
return render(request, 'todo/todo.html',mycontext)
in my urls.py: , i have set path('todo/',views.mytodo, name='todolist')
As you can see, after saving form, mycode will redirect to /todo/ which is automatically refreshed after every submit and a fresh form comes again everytime. If you are a good django dev, you will understand what I did. Make sure to reply if you have any queries .Thanks :)
When we reload the page (F5 or ctrl+shift+R), it submits the previously sent data. so instead of refreshing, we will directly hit the url using return HttpResponseRedirect('/posts/')
This way it will show the page with empty form (now even if you refresh it will only show data, won't submit it previous data again)
from django.shortcuts import render, HttpResponseRedirect
from django.views import View
from .models import Post
from .forms import PostForm
class PostListView(View):
def post(self, request, *args, **kwargs):
posts = Post.objects.filter(author=request.user).order_by('-created_on')
form = PostForm(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = request.user
new_post.save()
return HttpResponseRedirect('/posts/')
context = {
'post_list': posts,
'form': form
}
return render(request, 'social/post_list.html', context)