I'm trying to make register possible on the homepage, so I don't have a seperate URL to handle registration. I'm trying to send the form through get_context_data, however it's not working. Here's my code:
forms.py
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = [
'username',
'password',
]
views.py
class BoxesView(ListView):
template_name = 'polls.html'
def get_context_data(self):
context = super(BoxesView, self).get_context_data()
# login
if self.request.method == 'POST':
form = UserRegistrationForm(self.request.POST or None)
context['form'] = form
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.create_user(username=username, password=password)
user.save()
return redirect('/')
else:
print(form.errors) #doesn't print anything
print(form.non_field_errors()) #doesn't print anything
print('Errors') #doesn't print anything
else:
form = UserRegistrationForm()
context['form'] = form
return context
def get_queryset(self):
pass
base.html
<form action="" enctype="multipart/form-data" method="post">{% csrf_token %}
<div class="registerBox">
{{ form.username }}
{{ form.password }}
<input type="submit" value="register"/>
</div>
</form>
So when I submit the form it gives this error: Method Not Allowed (POST): "POST / HTTP/1.1" 405 0
And it isn't creating a new User. Any idea what the problem is?
EDIT: Tried FormMixin, got this error: The view app.views.BoxesView didn't return an HttpResponse object. It returned None instead.
class BoxesView(ListView):
template_name = 'polls.html'
form_class = UserRegistrationForm
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.create_user(username=username, password=password)
user.save()
return redirect('/')
def get_context_data(self):
context = super(BoxesView, self).get_context_data()
context['form'] = self.get_form()
return context
def get_queryset(self):
pass
Ok I see the issue fix the indentation, your if statement should be inside the get_context_data function not outside ;)
You need to add post() method and FormMixin to your CBV like this:
class BoxesView(FormMixin, ListView):
template_name = 'polls.html'
form_class = UserRegistrationForm
# ...
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# ...
else:
# ...
return render(request, self.template_name, {'data': some_data})
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'm trying to populate my ModelForm with some of data that I have submitted to previous HTML page which is also ModelForm.
I just want to pass it to another form so it doesn't have to be written twice.
I've tried couple solutions from stackoverflow but they are 6+ years old, kinda outdated and also couldnt come up with solution from django docs https://docs.djangoproject.com/en/2.2/topics/forms/
I have two models, which have same fields which are name and boxid
I need to pass it from first input to second(to populate it).
forms.py
class NewCashierForm(forms.ModelForm):
class Meta:
model = Cashier
fields = ("cashier_company", "cashier_dealer", "cashier_name", "cashier_boxid", "cashier_type", "cashier_package", "cashier_otheritem", "cashier_otheritemserial", "cashier_length", "cashier_promotion", "cashier_amount", "cashier_paymenttype")
labels = {"cashier_company":('Firma'), "cashier_dealer": ('Diler'), "cashier_name":('Ime i prezime'), "cashier_boxid":('Box ID'), "cashier_type":('Tip'), "cashier_package":('Paket'), "cashier_otheritem":('Drugi uredjaj'), "cashier_otheritemserial":('SBU'), "cashier_length":('Dužina'), "cashier_promotion":('Promocija'), "cashier_amount":('Iznos'), "cashier_paymenttype":('Nacin uplate')}
exclude = ['cashier_published']
def save(self, commit=True):
cashier = super(NewCashierForm, self).save(commit=False)
if commit:
cashier.save()
return cashier
class NewPersonForm(forms.ModelForm):
class Meta:
model = Person
fields = {"person_name", "person_adress", "person_phone", "person_boxid"}
labels = {"person_name":('Ime i prezime'), "person_adress":('Adresa'), "person_phone":('Telefon'), "person_boxid":('Box ID')}
def save(self, commit=True):
person = super(NewPersonForm, self).save(commit=False)
if commit:
person.save()
return person
views.py
def addcashier(request):
if request.method == 'GET':
form = NewCashierForm()
else:
form = NewCashierForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/byauthor')
return render (request, 'main/addcashier.html', {'form':form})
def addperson(request):
if request.method == 'GET':
form = NewPersonForm()
else:
form = NewPersonForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/addcashier')
return render (request, 'main/addperson.html', {'form':form})
addperson.html and addcashier.html
{% extends "main/base.html" %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn" type="submit">Unos i dodavanje pretplate</button>
</form>
<input type="button" value="Otkazi unos" onclick="window.history.back()" />
{% endblock %}
Any help and/or hint is appreciated.
To prepopulate the form, you need to pass an argument initial={} when initializing your form for the GET call. Since you are passing data from one view to another, you should use sessions.
def addperson(request):
if request.method == 'GET':
form = NewPersonForm()
else:
form = NewPersonForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
request.session["person_form"] = request.POST.dict() #save the form as a dict in request.sessions
return redirect('/addcashier')
return render (request, 'main/addperson.html', {'form':form})
Then in your second view, use this data from sessions to initialize the form.
def addcashier(request):
if request.method == 'GET':
# get the form data from the request.session
form_data = request.session.pop('person_form', {})
box_id = form_data.get("person_boxid")
name = form_data.get("person_name")
form = NewCashierForm(initial={"cashier_name":name, "cashier_boxid":box_id}) # initialize the form with the data
else:
form = NewCashierForm(request.POST)
if form.is_valid():
fs = form.save(commit=False)
fs.user = request.user
fs.save()
return redirect('/byauthor')
return render (request, 'main/addcashier.html', {'form':form})
sorry I just learned programming
I have registration template and registration success template.
after user fill the form registration, it will be redirect to registration success template
How to get input value['email'] from registration template and display it in registration success template ?
views.py
class RegisterFormView(TemplateView):
template_name = 'home/register.html'
def get(self, request):
user_form = UserForm()
return self.render_to_response({
'base_url' : settings.BASE_URL,
'user_form': user_form,
})
def post(self, request, *args, **kwargs):
if request.method == 'POST':
user_form = UserForm(request.POST)
if user_form.is_valid():
user = user_form.save()
user.full_name = user_form.cleaned_data.get("full_name")
user.email = user_form.cleaned_data.get("email")
user.phone_number = user_form.cleaned_data.get("phone_number")
user.set_password(user_form.cleaned_data.get("password"))
password = user_form.cleaned_data.get("password")
profile = Profile.objects.create(created_by=user)
user.save()
return redirect(reverse("home:register-success"))
else:
user_form = UserForm(request.POST)
return self.render_to_response({
'base_url' : settings.BASE_URL,
'user_form': user_form,
})
register.html
{{user_form|crispy}}
register-success.html
we have send email activation to {{user.email}}
EDIT
register views.py
class RegisterSuccessView(TemplateView):
template_name = 'home/register-success.html'
def get(self, request):
return self.render_to_response({
'base_url' : settings.BASE_URL,
})
Please try replace
return redirect(reverse("home:register-success"))
to
return render(self.request, template_name='home/register-success.html', context={'user': user})
If you want to have another URL for register-success.html you can create an additional view with page and context return
I've been trying to render HTML using form I made in forms.py, however this is not working, just fail to loaded without error message. Also, there is no message in console too, so I'm having hard time to fix this. Please take a look and tell me which part is wrong.
This is urls.py
url(r'^profile/(?P<username>[-\w.]+)/$', views.profile, name='profile'),
url(r'^password_change/(?P<username>[-\w.]+)/$', views.password_change, name='password_change'),
url(r'^password_change_done/$', views.password_change_done, name='password_change_done'),
forms.py
class PasswordChangeForm(forms.Form):
oldpassword = forms.CharField(max_length = 20, widget=forms.TextInput(attrs={'type':'password', 'placeholder':'your old Password', 'class' : 'span'}))
newpassword1 = forms.CharField(max_length = 20, widget=forms.TextInput(attrs={'type':'password', 'placeholder':'New Password', 'class' : 'span'}))
newpassword2 = forms.CharField(max_length = 20, widget=forms.TextInput(attrs={'type':'password', 'placeholder':'Confirm New Password', 'class' : 'span'}))
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self._meta.model.USERNAME_FIELD in self.fields:
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus': True})
def clean(self):
if 'newpassword1' in self.cleaned_data and 'newpassword2' in self.cleaned_data:
if self.cleaned_data['newpassword1'] != self.cleaned_data['newpassword2']:
raise forms.ValidationError(_("The two password fields did not match."))
return self.cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
views.py
#login_required
def password_change(request, username):
if request.method == 'POST':
form = PasswordChangeForm(data=request.POST, user=request.user)
print("username is "+username)
if form.is_valid():
#form.save()
update_session_auth_hash(request, form.user)
form.save()
print("A")
return HttpResponseRedirect('/blog/password_change_done/')
else:
update_session_auth_hash(request, form.user)
form.save()
print("B")
return redirect(reverse('blog:profile', args=[form.user.get_username()]))
else:
print("C")
form = PasswordChangeForm(user=request.user)#unbound
return redirect(reverse('blog:profile', args=[form.user.get_username()]))
profile.html (where password_change should be loaded.)
<h2>password change</h2>
{% if error_message %}
<p><strong>{{ error_message }}</strong></p>
{% else %}
{% endif %}
<form class="form-horizontal" role="form" action="{% url 'blog:password_change' user.username %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="button-primary">password change</button></div>
</form>
Here is one of the examples how change_password view can be implemented
from django.contrib import messages
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
from django.shortcuts import render, redirect
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
messages.success(request, 'Your password was successfully updated!')
return redirect('accounts:change_password')
else:
messages.error(request, 'Please correct the error below.')
else:
form = PasswordChangeForm(request.user)
return render(request, 'accounts/change_password.html', {
'form': form
})
Have a look at the part related to GET request. There you have to create the form and render the page with context. You should provide form to the context.
In your case you create the form and redirect to the profile page straight after that. That is why you see empty form page.
I have this line of code in models.py
#login_required
def user_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/loggedin')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render(request, 'profile.tml', args)
and in forms.py
from django import forms
from models import UserProfile
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('address', 'contactnos')
in my loggedin.html
{% extends 'base.html' %}
{% block content %}
<h2> Hi {{ full_name }} you are logged in!</h2>
<p>click here to logout. </p>
<p>Click here to edit your profile info </p>
{% endblock %}
The error is says:
ValueError at /profile/
The view userprofile.views.user_profile didn't return an HttpResponse object.
You haven't accounted for a GET scenario; what happens when the view is not accessed via POST? You return nothing, therefore you get this error.
Return an http.HttpResponse when request.method != 'POST'
Perhaps you meant to un-indent the bottom half else statement.
You haven't accounted for a GET scenario; what happens when the view is not accessed via POST? You return nothing, therefore you get this error.
Return an http.HttpResponse when request.method != 'POST'
Perhaps you meant to un-indent the bottom half else statement.
#login_required
def user_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/loggedin')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render(request, 'profile.tml', args)