Django - Form bind data after initialization - django

I have a user model for which I'm trying to make a view that manages both create/update form rendering/post.
Here is the view that I did for now
def user_edit(request, user_id=None):
obj = {}
status = 200
if user_id:
user = get_object_or_404(User, pk=user_id)
else:
user = User()
user_form = UserForm(instance=user, prefix='user')
if request.method == 'POST':
user_form = UserForm(request.POST, instance=user, prefix='user')
if user_form.is_valid():
user_form.save()
else:
status = 406
obj['user_form'] = user_form
return render(request, 'user/edit.html', obj, status=status)
This works fine, but as you can see, my user_form is initialized 2 times. In order to make this more DRY, at POST time I'd like to update the form definition instead of redefining it. Something like:
if request.method == 'POST':
user_form.data = request.POST
user_form.prefix = 'user'
But I can't make this work. So 2 questions:
Does my view seem valid ?
How can I avoid the form re-definition ?

I would just restructure a couple of lines this way:
def user_edit(request, user_id=None):
status = 200
if user_id:
user = get_object_or_404(User, pk=user_id)
else:
user = User()
user_form = UserForm(request.POST or None, instance=user, prefix='user')
if request.method == 'POST':
if user_form.is_valid():
user_form.save()
else:
status = 406
return render(request, 'user/edit.html', {'form': user_form}, status=status)
Sometimes, it makes sense to duplicate may be 1 line of code to keep it readable.

You should use if condition like this to initialize form only once:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {
'form': form,
})
Taken from documentation of django. If you are new to Python, it may seem strange to define a variable in if..else statement, but it is pretty common and valid way in Python.

Related

Form Django can't update data

here's my code
i can't update my data if i used type "file"
My form can't be updated, I try print(form.error) but "this field requirement" appears, even though the form is filled out
views.py :
#login_required
def data_karyawan_edit(request, id):
karyawan = Karyawan.objects.get(id=id)
ar_divisi = Divisi.objects.all()
if request.method == 'POST':
form = KaryawanForm(request.POST, request.FILES, instance=karyawan)
print(form.errors)
if form.is_valid():
form.save()
messages.success(request, "Berhasil disimpan")
return redirect('data_karyawan')
else:
form = KaryawanForm()
return render(request, "data_karyawan/edit.html", {'karyawan': karyawan, 'ar_divisi': ar_divisi})

Where should I place my context for the forms_as.p to work properly

basically, i'm trying to form.as_p to list the values but its not working. Its not really that its not working, but it only works (it only appears in my template) after i press submit. I believe I have placed the context in the wrong place or the wrong indentation but im not sure where i should shift context['form'] = form to. I tried to shift it but it says that lcoal variable referenced before assignment. Could someone advise?
The reason why I put it below else is because i want to display the errors if there are errors
def create_blog_view(request):
context = {}
user = request.user
if request.method == 'POST':
form = CreateBlogPostForm(request.POST or None, request.FILES or None)
if form.is_valid():
obj= form.save(commit = False)
author = Account.objects.filter(email=user.email).first()
obj.author = author
obj.save()
return redirect('HomeFeed:main')
else:
context['form'] = form
return render(request, "HomeFeed/create_blog.html", context)
def create_blog_view(request):
context = {}
user = request.user
form = CreateBlogPostForm()
if request.method == 'POST':
form = CreateBlogPostForm(request.POST or None, request.FILES or None)
if form.is_valid():
obj= form.save(commit = False)
author = Account.objects.filter(email=user.email).first()
obj.author = author
obj.save()
return redirect('HomeFeed:main')
else:
context['form'] = form
context['form'] = form
return render(request, "HomeFeed/create_blog.html", context)
Before your if statement, you need to add:
form = CreateBlogPostForm()
context['form'] = form
This will initialise a blank form and add it to your context.
Currently, you are only adding 'form' to your context, if the form has been submitted (/ a POST request has been sent to the view) and the form has validation errors.
To avoid repeated code, better still would be something like this:
def create_blog_view(request):
user = request.user
form = CreateBlogPostForm()
if request.method == 'POST':
form = CreateBlogPostForm(request.POST, request.FILES)
if form.is_valid():
obj= form.save(commit = False)
author = Account.objects.filter(email=user.email).first()
obj.author = author
obj.save()
return redirect('HomeFeed:main')
context['form'] = form
return render(request, "HomeFeed/create_blog.html", context)

Form validation in function views

Is there any difference between the 2 parts of code?
def test(request):
if request.method == 'POST':
form = TestForm(request.POST)
if form.is_valid():
form.save()
title = form.cleaned_data.get('title') #<<--
print(title)
return redirect('blog-home')
else:
form = TestForm()
return render(request, 'blog/test.html', {'form': form})
def test(request):
if request.method == 'POST':
form = TestForm(request.POST)
if form.is_valid():
form.save()
title = request.POST.get('title') # <<--
print(title)
return redirect('blog-home')
else:
form = TestForm()
return render(request, 'blog/test.html', {'form': form})
Since both cases is part of form.is_valid() condition I believe it should be same right?
No, it does not necessary produce same result as cleaned_data documentation states
Each field in a Form class is responsible not only for validating
data, but also for “cleaning” it – normalizing it to a consistent
format. This is a nice feature, because it allows data for a
particular field to be input in a variety of ways, always resulting in
consistent output.

AttributeError at /basic_app/register/ : 'tuple' object has no attribute 'get'

I know this question have been asked alot and most of the time its due to render or HttpResponse in the views.py, i double checked mine but the code looks good to me, dont know where the problem is.
This is a views.py file for a very basic django form but i can't get it to work
def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(data = request.POST)
profile_form = UserProfileInfoForm(data = request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_from.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit = False)
profile.user = user
if 'profile_pic' in request.FILES:
profile.profile_pic = request.FILES['profile_pic']
profile.save()
registered = True
else:
return (user_form.errors,profile_form.errors)
else:
user_form = UserForm()
profile_form = UserProfileInfoForm()
return render(request,'basic_app/register.html',{'user_form': user_form,
'profile_form':profile_form,
'registered':registered})
You can not return (user_form.errors, profile_form.errors), since that is not a HttpResponse object. What response should the server return in that case.
Usually in case the form is invalid, the server will rerender the content. The form will, if you render it properly display the errors.
Note that in case the POST request was successful, you usually should redirect to implement the Post/Redirect/Get pattern [wiki]. You furthermore probably want to use a UserCreationForm [Django-doc]. This will set the password of the user in the correct way (with .set_password(..)), and run a password validator if you configured this.
You thus can rewrite your view as follows, but you probably should replace UserForm with UserCreationForm:
from django.shortcuts import redirect
def register(request):
if request.method == 'POST':
user_form = UserForm(data=request.POST)
profile_form = UserProfileInfoForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_from.save(commit=False)
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'profile_pic' in request.FILES:
profile.profile_pic = request.FILES['profile_pic']
profile.save()
return redirect('name-of-view')
else:
user_form = UserForm()
profile_form = UserProfileInfoForm()
return render(
request,
'basic_app/register.html',
{'user_form': user_form, 'profile_form':profile_form })

Saved model is None

So i have a model that i'm trying to save using a form which submits successfully, but in the Admin the object value None. I know the problem is in the views but i can't figure it out. Here's my code:
Views.py
def profilecreate(request):
if request.method == 'GET':
form = ProfileForm()
else:
form = ProfileForm(request.POST)
if form.is_valid():
description = form.cleaned_data['description']
caption= form.cleaned_data['caption']
photo = form.cleaned_data['photo']
profile = Profile.objects.create(description=description, caption=caption, photo=photo)
return HttpResponseRedirect(reverse('profile-id', kwargs={'profile_id': profile.id}))
return render(request, 'profile_form.html', {'form': form})
Someone please assist
Second view attempt
def profilecreate(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = ProfileForm(request.POST)
# check whether it's valid:
if form.is_valid():
photo = form.cleaned_data['photo']
description = form.cleaned_data['description']
caption = form.cleaned_data['caption']
form.save(commit=True)
return HttpResponseRedirect('/')
# if a GET (or any other method) we'll create a blank form
else:
form = ProfileForm()
return render(request, 'profile_form.html', {'form': form})