Django form.is_valid() not validating - python

I can't seem to get my form to validate with .is_valid()
The associated View
def edit_social_media(request, user_id):
#verify a user is allowed to access the page
# user is saving the form
if request.POST:
form = SocialMediaForm(request.POST)
if form.is_valid():
...
return HttpResponseRedirect(reverse(users.profile,
args = (request.user.id,)))
# displaying the initial form
else:
try:
form = SocialMediaForm(instance = SocialMedia.objects.get(user = request.user))
except SocialMedia.DoesNotExist:
form = SocialMediaForm()
return render_to_response('users/edit_social_media.html', {'form': form, 'user' : user},
context_instance = RequestContext(request))
Forms.py
class SocialMediaForm(forms.ModelForm):
class Meta:
model = SocialMedia
fields = {'twitter', 'facebook', 'linkedin'}
the template
<form method="post" action=''>
{% csrf_token %}
<div class="form-col-left">
<p>
{{ form.as_p }}
</p>
</div>
<div class="submitbutton">
<input type="submit" class="green button" value="Save Social Media Settings" />
</div>
</form>
The model
from django.db import models
from django.contrib.auth.models import User
class SocialMedia(models.Model):
class Meta:
app_label = 'dashboard'
#the user associated with the data
user = models.ForeignKey(User)
#Twitter, Facebook, and Linkedin pages
twitter = models.URLField("Twitter")
facebook = models.URLField("Facebook")
linkedin = models.URLField("Linkedin")
Some help would be greatly appreciated. I'm rather new to django so I've been missing some of the smaller nuances. I've found some similar issues on stackoverflow related to bound vs unbound forms, but from reading the documentation I think I properly bound the data to the form.

Since this is a ModelForm it would help seeing the actual Model that this form is using, the SocialMedia class. Also I am not sure what you mean by not getting it to validate but I am assuming that the is_valid() method returns False.
My guess is that the method returns False because you are not setting the user on the form. In the POST dictionary you don't have the user or user_id. Add the following line before the "if form.is_valid()" statement:
form.instance.user = self.request.user
Hope this works.

Related

Operation of standard Django view, form and template code to update a model

I am trying to understand how a very regularly used code-form in Django views.py actually works. I see the following (or a variation) used a lot, but I can’t find a line-by-line explanation of how the code works – which I need if I am to use it confidently and modify when needed.
Can you let me know if I have understood how Django processes these various components? If not, please indicate where I have misunderstood.
I will start with the model then introduce urls.py the view and the form. I will go through the relevant parts of the code. I will consider:
The model:
#models.py
class CC_Questions(models.Model):
# defining choices in Model : https://docs.djangoproject.com/en/4.0/ref/models/fields/
personality = [
('IF','IF'),
('IT','IT'),
('EF','EF'),
('ET','ET'),
]
q_text = models.CharField('Question text', max_length=200)
#C1_Type = models.CharField('Choice 1 Type', max_length=2)
C1_Type = models.CharField(choices=personality, max_length=2)
Choice1_text = models.CharField('Choice 1 text', max_length=100)
#C2_Type = models.CharField('Choice 2 Type', max_length=2)
C2_Type = models.CharField(choices=personality, max_length=2)
Choice2_text = models.CharField('Choice 2 text', max_length=100)
#
def __str__(self):
return self.q_text[:20]
The url
#
#urls.py
app_name = ‘polls’
urlpatterns = [
…..
# ex: /polls/p2create
path('p2create/', p2_views.p2create, name='p2create'),
…
The view:
#views.py
from.forms import Anyform
#
def p2create(request):
if request.method == 'POST':
form = AnyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/polls/p2')
else:
form = AnyForm()
context = {'form' : form}
return render(request, 'pollapp2/create.html', context)
#
The form:
#forms.py
#
….
from django import forms
from .models import ….. CC_Questions …
…….
class AnyForm(forms.ModelForm):
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The template:
#
# Create.html
#
…..
{% load widget_tweaks %}
…..
<form method="POST">
{% csrf_token %}
…
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
<label for="C1_type">Type 1</label>
{% render_field form.C1_Type class="form-control" %}
…….
Does the code operate as follows?
The user enters URL in browser: http://localhost:8000/polls/p2create/
The urls.py picks the view to execute
path('p2create/', p2_views.p2create, name='p2create'),
views.py runs the view:
def p2create(request):
Now, as no form has yet been "identified" or "loaded" (??) the following test fails:
if request.method == 'POST':
so the Else clause executes
else:
form = AnyForm()
that "sets" the variable form to "AnyForm()"
The following line creates a dictionary, named context, that creates a key 'form' that is linked with the value form (=Anyform)
context = {'form' : form}
The following line searches for create.html in the template directory, passing the directory context as a parameter
return render(request, 'pollapp2/create.html', context)
Then template, create.html, displays various input boxes (??) from :
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
<form method="POST">
...
<div class="col-lg-4">
<button type="submit" class="btn btn-info">Submit</button>
</div>
...
</form>
the view is executed again (????) , but this time request method is set to "POST" because of the form method="POST" in the template (?????)
if request.method == 'POST':
Now the same form , AnyForm , is "reloaded" (????) but with the parameter value "POST"
if request.method == 'POST':
form = AnyForm(request.POST)
Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
if form.is_valid():
then all the values "captured" in the template by (???)
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
(??????)
are written by the view (?????)
form.save
to the corresponding fields in the ModelForm (?????)
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The view then redirects and loads the home page in the browser
return redirect('/polls/p2')
Ok, So with the help of the references (mentioned below) and the workflow suggested by you, let us first see, the Django MVT workflow and then address the various questions asked in between the post.
WebForms with Django MVT in brief:
Prepare data of several different types for display in a form.
Render data as HTML.
Edit, enter data using a convenient interface.
Validate and clean up the data.
Return data to the server.
Save, delete or pass on for further processing.
The URL:
When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute. Which is handled by our views.py. From the frontend, if the request is not 'POST', then it is a GET request, hence the else part of the handling function in the views.py executes. This you have mentioned already.
The View: - Form data sent back to a Django website is processed by a view, generally, the same view which published the form. This allows us to reuse some of the same logic. To handle the form we need to instantiate it in the view for the URL where we want it to be published.
If we use request.POST, as in this line:
form = AnyForm(request.POST)
It transforms the form into a bound form (Form bound with data). Read more about it here.
Questioned By You (QBY) - When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
So, yes, If the action attribute is not mentioned in the form then the data will be passed to the view responsible for displaying the form.
QBY - the view is executed again (????), but this time request method is set to "POST" because of the form method="POST" in the template (?????)
The button type submit, submits the form and make the request a POST request. The Django template sends that submitted data in the request.POST.
QBY - Now the same form, AnyForm, is "reloaded" (????) but with the parameter value "POST"
Here, if the return method at the end of the POST condition is HttpResponseRedirect it will redirect the user to the mentioned URL page, but if the same HTML is used to be rendered then the form will be displayed as a bound form. (It depends upon the requirements)
QBY - Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
Form.is_valid()
The primary task of a Form object is to validate data. With a bound Form instance, call the is_valid() method to run validation and return a boolean designating whether the data was valid. If yes, then the data is being saved in the model.
QBY - then all the values "captured" in the template by (???)
All the values are sent to views in the request.POST. We can check it by
print(request.POST)
QBY - are written by the view (?????), form.save to the corresponding fields in the ModelForm (?????)
Save method is called on the Django ModelForm instance in order to save the data to the database. Calling save would run the validation check. A ValueError will be raised if the data in the form doesn't validate.
This saved data can now be processed further.
References:
[https://docs.djangoproject.com/en/4.0/topics/forms/][2]
[https://www.tangowithdjango.com/book/chapters/models_templates.html][3]
[https://docs.djangoproject.com/en/4.0/ref/forms/api/][4]

How to take boolean field input from HTML into my Django model?

I have used Django forms for creating users and I extended the default User model by adding a boolean field, so I defined a new form for it. But I couldn't take input from HTML form to this boolean field. Shall I change my HTML form code?
Following are my code samples:
models.py
# accounts.models.py
from django.db import models
from django.contrib.auth.models import User
class SpecialUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
flag = models.BooleanField()
def __str__(self):
return self.title
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.forms.widgets import CheckboxInput
from .models import SpecialUser
class RegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
class SuperUserForm(forms.ModelForm):
class Meta:
model = SpecialUser
fields = ['flag']
widgets = {
'flag': CheckboxInput(attrs={'class': 'flag'}),
}
views.py
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
sp_form = SuperUserForm(request.POST)
if form.is_valid() and sp_form.is_valid():
user = form.save()
sp_form = sp_form.save(commit=False)
sp_form.user = user
sp_form.save()
messages.success(request, 'Account created!')
return redirect('login')
else:
form = RegisterForm()
sp_form = SuperUserForm()
messages.warning(request, 'Your account cannot be created.')
return render(request, 'register.html', {'form': form})
HTML form code:
<form method="post" class="form-group">
{% csrf_token %}
{{ form|crispy }}
<label for="flag">Special User: </label>
<input id="flag" class="flag" type="checkbox" name="flag">
<button type="submit" class="btn btn-success">Sign up</button>
</form>
In your views.py you're creating a local variable for a SpecialUser form, sp_form, that is neither loaded into the context data nor templated in the HTML form code.
You can load sp_form into the context data by adding it to the context dict passed to render(). This will allow the template to see the variable. For example:
return render(request, 'register.html', {'form': form, 'sp_form': sp_form})
And then you can render it in the template. For example, underneath the main form:
{{ form|crispy }}
{{ sp_form|crispy }}
For starters this is generally not how you would want to extend the user model in a Django application. You would want to inherit from AbstractUser and add your fields to that model and run migrations. At least in this case, that would be ideal, then you could simply define the field on your RegisterForm.fields and let {{ form|crispy }} render the form for you. Naturally, you could call form.save() and move on with your life.
To clarify why this may not be working, it is generally not good practice to render your own fields for a form unless absolutely necessary. If you insist on doing it this way, note that Django prefixes the id with id_ so in your case it would be <label for="id_name">...</label> and <input id="id_flag" ...

How to update profile picture for existing model instance?

I'm trying to update a user's profile photo after they've already created their account. I'm using an abstract user model connected to a model called Person. For additional context, I have my application connected to AWS to deploy to Heroku.
I have a form, model, url and view set up but I'm sure I'm missing some piece to the puzzle.
<form action="{% url 'update-photo' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table class="table-form">
{{ form|crispy }}
</table>
<input type="submit" class="btn btn-lg custom-bg">
<br><br>
</form>
class User(AbstractUser):
is_active = models.BooleanField(default=False)
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
upload = models.FileField(default='core/static/images/default_avatar.png')
class UpdatePhotoForm(forms.ModelForm):
class Meta:
model = Person
fields = ('upload',)
#login_required
def update_photo(request):
person = Person.objects.get(user=request.user)
from core.forms import UpdatePhotoForm
if request.method == "POST":
form = UpdatePhotoForm(data=request.POST, instance=request.user.person)
if form.is_valid():
person = form.save(commit=False)
person.save()
return redirect('profile')
else:
form = UpdatePhotoForm()
return render(request, 'core/edit_profile.html', {'form': form})
path('update_photo/', core_views.update_photo, name='update-photo'),
The form submits without any error but does not actually update the user's photo. I can change the photo in the admin site but not via the form. Any help would be greatly appreciated!
You will have to fetch file field from request Object with following code:
form = UpdatePhotoForm(data=request.POST, files=request.FILES, instance=request.user.person)

How can i use more different forms in the same Django template?

In my project, i have a template where i'm trying to put two forms for different use cases. I've never come across this problem before, so i don't really know where to go from here to use two forms in the same page.
At first i thought of creating another view to handle each form, but i think that this solution would create problems with the rendering of my templates, other than not being sustainable if i should have this problem again with another template.
After making some research, i found a solution but it works for class based views, but i'd like to avoid that since my view is already a function based view, and i would have to make a lot of changes in my code.
Would it be possible to solve this problem with a function based view? Every advice is appreciated
First field
class FirstForm(forms.ModelForm):
firstfield = forms.CharField()
secondfield = forms.CharField()
class Meta:
model = MyModel
fields = ("firstfield", "secondfield")
def save(self, commit=True):
send = super(FirstForm, self).save(commit=False)
if commit:
send.save()
return send**
Second Form
class SecondForm(forms.ModelForm):
firstfield = forms.FloatField()
secondfield = forms.Floatfield()
thirdfield = forms.CharField()
class Meta:
model = MyModelTwo
fields = ("firstfield", "secondfield", "thirdfield")
def save(self, commit=True):
send = super(SecondForm, self).save(commit=False)
if commit:
send.save()
return send
Template
<h3> First Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
<h3> Second Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
EDIT: my view:
def myview(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 = FirstForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
send = form.save()
send.save()
messages.success(request, f"Success")
# if a GET (or any other method) we'll create a blank form
else:
form = FirstForm()
return render(request,
"main/mytemplate.html",
context={"form":form})
This answer is a bit general because you haven't included your view function. You can add each of these forms to your view's context. Something like this:
views.py
...
from .forms import FirstForm, SecondForm
...
def some_view(request):
context = {
'first_form': FirstForm(request.POST or None),
'second_form': SecondForm(request.POST or None)
}
return render(request, "app/some_template.html", context)

Updating a variable value of a user object in Django

I am trying to implement some functionality that allows a user to edit their personal information in a Django project using Django forms. When a user enters the new value in the form and hits enter, they are brought back to the main profile page which is correct however, the values remain the same as before. Below is how I have tried to implement the functionality:
Forms
class UpdateProfile(forms.ModelForm):
email = forms.EmailField(required=False)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
age = forms.IntegerField(required=False)
height = forms.IntegerField(required=False)
weight = forms.IntegerField(required=False)
class Meta:
#Here are the fields that i want editable
model = User
fields = ('email', 'first_name', 'last_name', 'age', 'height', 'weight')
#Here im trying to commit the changes to the user and return the user
def save(self, commit=True):
super(UpdateProfile, self).__init__(commit)
if commit:
user.save()
return user
Views
def update_profile(request):
args = {}
if request.method == 'POST':
form = UpdateProfile(request.POST, instance=request.user)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('account/profile.html'))
else:
form = UpdateProfile()
args['form'] = form
return render(request, 'account/edit_profile.html', args)
HTML
% block head %}
<title>Profile</title>
{% endblock %}
{% block body %}
<div class="container">
<form method="POST" action="{% url 'account:profile' %}">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
{% endblock %}
Your form is submitting directly to the view profile page. But that page is presumably not expecting to validate a form. You need to submit it back to the update_profile page, which you normally do by using an action of just "." in the form HTML element.
<form method="POST" action=".">
Once you've done that, you'll see some issues with your form save() method. That method does not do anything useful anyway; you should remove it and let the superclass one be called automatically.
This line seems wrong:
super(UpdateProfile, self).__init__(commit)
You're calling __init__ from the parent class, but the method being called is save()... Also you're refering to a user variable which is (hopefully) not defined in this scope.

Categories